View on GitHub

Cviceni-React-Firestore

Úkol na procvičení komunikace Reactu a Firestore databáze.

Cvičení: Firestore nákupní seznam

Úvod

Firestore je online databáze z balíčku služeb Firebase od Googlu, kterou volně můžete zapojit do vašich projektů. Vaši uživatelé pak budou mít přístup k těm nejčerstvějším datům podobně jako když stahují pomocí fetch. Zároveň jim můžete dovolit i data zapisovat a sdílet mezi sebou.

V následujícím cvičení si vyzkoušíte vytvořit malou aplikaci pro nákupní seznam.

Dokumentace Firestore: https://firebase.google.com/docs/firestore

demo

Zadání

  1. Založ nový projekt pomocí create-czechitas-app.

  2. Přichystej komponentu NakupniSeznam.

    1. Vlož ji do stránky.
    2. Přidej komponentě stav s polem objektů.

      const [polozky, setPolozky] = useState([
      	{
      		nazev: 'První hardcodovaná položka',
      	},
      	{
      		nazev: 'Druhá hardcodovaná položka',
      	},
      ])
      
    3. Vypiš položky do odrážkového seznamu.

      <ul>
         {polozky.map((polozka) => (
            <li key={polozka.nazev}>{polozka.nazev}</li>
         ))}
      </ul>
      
    4. Zkontroluj v prohlížeči. Měl by se na stránce ukázat seznam o dvou položkách.
  3. Ve Firebase konzoli (https://console.firebase.google.com/) přidej nový projekt. Pojmenuj ho například Nakupni seznam. Stiskni tlačítko Continue.

  4. V kroku Google Analytics for your Firebase project zruš zapnuté Analytics. Nejsou důležité a komplikují vytvoření první aplikace.

  5. V projektu přejdi v levém menu do záložky Firestore.

    1. Stiskni tlačítko Create database.
    2. Zaškrtni Start in test mode.
    3. V dalším kroku pro polohu zvol eur3 (europe-west).
  6. Vytvoř kolekci pomocí Start collection s názvem seznam.

    1. V ní vytvoř první dokument s Auto-ID a s fieldem nazev=První položka.
    2. Přidej druhý dokument s názvem Druhá položka.
  7. V Project overview přidej Web app.

    1. Pojmenuj ji Nakupní seznam.
    2. Nezaškrtávej Firebase Hosting. Pro hostování jsme si ukazovali Netlify (video). Není potřeba se učit nový hosting.
    3. Register app.
    4. Nech si stránku otevřenou.
  8. V terminálu ve složce s projektem z kroku 1 nainstaluj Firebase SDK pomocí npm install firebase.
  9. Ve složce src vytvoř soubor db.js.

    1. Naimportuj v něm firebase.
      import firebase from 'firebase/app'
      import 'firebase/firestore'
      
    2. Zkopíruj do něj proměnnou firebaseConfig a firebase.initializeApp(firebaseConfig) z konzole v prohlížeči.
    3. Exportuj konstantu pro přístup k databázi export const db = firebase.firestore()
  10. Hardcodované položky nahraď za položky z db.

    1. Naimportuj do komponenty soubor db.js.
    2. Přidej useEffect, který se vykoná jen při prvním renderu.

      useEffect(() => {}, [])
      
    3. V efektu poslouchej na změny v kolekci seznam.

      db.collection('seznam').onSnapshot((querySnapshot) => {})
      
    4. Při přijetí změn (onSnapshot) aktualizuj stav polozky.

      setPolozky(querySnapshot.docs.map((doc) => doc.data()))
      
    5. Nastav počáteční stav na prázdné pole. Smaž hardcodované objekty.
  11. Zkus ve Firebase konzoli přidat do seznamu novou položku. Měla by se ihned objevit i na tvé stránce.

  12. useEffect by si po sobě měl i uklidit pro případ, že se komponenta odpojí ze stránky.

    1. db.collection('seznam').onSnapshot vrací funkci pro vypnutí poslouchání změn. Pokud ji v useEffect vrátíme, React ji ve správný čas spustí a vypne tím posluchač. Přidej před zmíněný kód return.
  13. Vytvoř formulář na přidávání položek.

    1. Přidej stav pro textové políčko const [nazev, setNazev] = useState('').
    2. Přidej formulář.

      <form>
      	<label>
      		Název:{' '}
      		<input
      			value={nazev}
      			onChange={(event) => setNazev(event.target.value)}
      		/>
      	</label>
      	<button>Přidat</button>
      </form>
      
    3. V události onSubmit zamez přenačtení stránky pomocí event.preventDefault() při uložení.
    4. V události přidej nový dokument do kolekce.

      db.collection('seznam').add({ nazev })
      
    5. Vymaž text v textovém políčku.

      setNazev('')
      
  14. Seřaď položky podle data přidání.

    1. Při přidávání položky do seznamu ulož i aktuální datum.

      db.collection('seznam').add({
      	nazev,
      	datumVytvoreni: firebase.firestore.FieldValue.serverTimestamp(),
      })
      
    2. Nezapomeň na import firebase from 'firebase/app'.
    3. Přidej .orderBy('datumVytvoreni') do dotazu na položky.

      useEffect(() => {
      	return db
      		.collection('seznam')
      		.orderBy('datumVytvoreni')
      		.onSnapshot((querySnapshot) => {
      			setPolozky(querySnapshot.docs.map((doc) => doc.data()))
      		})
      }, [])
      
    4. Z výpisu ti zmizí staré položky, které datum nemají. Klidně je smaž ve Firebase konzoli.
  15. Přidej uživateli možnost označovat položky jako koupené.

    1. Při přidávání položky do seznamu jí nastav koupeno: false.
    2. V mapě pro výpis položek přidej checkbox.

      <input
      	type="checkbox"
      	checked={polozka.koupeno}
      	onChange={(event) => {}}
      />
      
    3. Při stahování položek si ulož do stavu id každé položky.

      setPolozky(
      	querySnapshot.docs.map((doc) => {
      		const data = doc.data()
      		data.id = doc.id
      		return data
      	}),
      )
      
    4. Po kliku na checkbox uprav koupeno v databázi.

      db.collection('seznam').doc(polozka.id).update({
      	koupeno: event.target.checked,
      })
      
    5. Devtools konzole ti u starých položek, které nemají vlastnost koupeno, bude hlásit chybu. Můžeš je pomocí Firebase konzole smazat.
  16. Přidej možnost položky mazat.

    1. Za název položky přidej tlačítko <button>smazat</button>.
    2. Po kliku odeber položku z databáze.

      db.collection('seznam').doc(polozka.id).delete()
      

Bonus

  1. Zapoj do projektu React Router.

    1. Na úvodní cestě měj výpis všech položek v seznamu a na druhé detail položky.

    2. Přidej switch podle ukázky. Vytvoř chybějící komponentu Detail, která pro zatím může vykreslovat jen nadpis <h1>Detail</h1>.

    <Switch>
    	<Route path="/detail/:id">
    		<Detail />
    	</Route>
    	<Route path="/">
    		<NakupniSeznam />
    	</Route>
    </Switch>
    
    1. Ve výpisu položek udělej ze všech názvů odkazy pomocí <Link>.
    <Link to={`/detail/${polozka.id}`}>{polozka.nazev}</Link>
    
    1. Nezapomeň naimportovat Link.
    import { Link } from 'react-router-dom'
    
  2. Do Detail přidej odkaz zpět na výpis.

    <Link to="/">Přejí na výpis</Link>
    
  3. Přidej do detailu výpis id.

    const { id } = useParams()
    
    <p>Id: {id}</p>
    
  4. Vyzkoušej si proklikat několik položek v seznamu. Sleduj, že id na stránce detailu se mění.

  5. Přidej do detailu stav pro data položky.

    const [polozka, setPolozka] = useState(null)
    
  6. Pomocí useEffect stáhni do stavu data.

    useEffect(() => {
    	return db
    		.collection('seznam')
    		.doc(id)
    		.onSnapshot((doc) => {
    			setPolozka(doc.data())
    		})
    }, [id])
    
  7. Data si cvičně můžeš v detailu vypsat třeba pomocí <pre>{JSON.stringify(polozka, null, 2)}</pre>.

  8. Všimni si, že data jsou před načtením na chvilku nastaveny na null. To protože stahování chvilku trvá.

  9. Pokud se data načítají, vypiš text Načítám. V opačném případě do nadpisu napiš {polozka.nazev}.

  10. Vypiš datum vytvoření položky.

    <div>
    	Datum vytvoření: {polozka.datumVytvoreni.toDate().toLocaleString()}
    </div>
    

Zabezpečení dat

Založená databáze v režimu test mode se po měsíci sama uzamkne a komunikace s ní z frontendové aplikace tak přestane fungovat. Co dělat dál je popsáno zde.