Reactï¼Gatsbyï¼+ Firebaseã§ãµã¼ãã¬ã¹å ¥éãã
å人éçºã§Firebase使ã£ã¦ãªããä½ããããªã¨ãããã¨ã§ãç´ æ¯ãã§ä½ã£ããã®ãå ¬éãã¦ã¿ã¾ãããReduxã®ãã¥ã¼ããªã¢ã«ã§ä½ãTodoã¢ããªãStoreãFirebaseã«ããä½ã§ä½ãå¤ãããã¤ã«ãªãã¾ããFirebaseã¯å¤ã®æ代ã«è§¦ã£ãã¨ãã¯åãªãPub/Subã§ããDBã ã£ãã®ã«ããããåºæ¥ãããã«ãªã£ã¦ã¦ãããã¾ããã触ãåã«å ¬å¼ã®ããã¥ã¡ã³ãããã£ã¨èªãã§codelabã試ãããããã®äºåç¥èã§ä½ãã¾ããã
ä½ã£ããã®
ãµã¤ãã¯ãã¡ããåã«æè¿éãã§ãã¨ããçç±ã ãã§Gatsby.jsã§ä½ã£ã¦ã¾ããNetlifyã§ãã¹ãã£ã³ã°ãã¦ã¾ãããç¹ã«Firebase hostingã使ã£ã¦ãªãçç±ã¯ããã¾ãããAuthã¨Firestoreã ã試ãããã£ãã®ã§ã
gatsby-firebase-todo.netlify.com
ã³ã¼ãã¯ãã¡ã
GitHub - rei-m/gatsby-firebase: Sample of Gatsby.js with Firebase
å®è£
Firebaseã®è¨å®
ä½ã¯ã¨ãããã¨ã³ããªã¼ãã¤ã³ãã§firebase.initializeApp
ãããªããã°ãããªãã®ã§ãããGatsbyã¯gatsby-browser.jsã«onClientEntry
ã¨ããAPIãçãã¦ããã®ã§ããã§è¡ãã¾ãããããã ãã§ããã
export const onClientEntry = () => { const config = { // your config }; firebase.initializeApp(config); };
Firebase.authentication
firebase.auth().onAuthStateChanged
ã«Observerãç»é²ãã¾ããããã¯ã¢ããªã±ã¼ã·ã§ã³å
¨ä½ã«å½±é¿ããã®ã§React.Contextã«èªè¨¼ç¶æ
ãä¿æãã¦ã¢ããªã±ã¼ã·ã§ã³å
¨ä½ãå²ãããã«ãã¾ããããããªæãã®custom hookãä½ã£ã¦åããuserãªãã¸ã§ã¯ããcontextã§æã¤ããã«ãã¦ã¾ãã
useFirebaseAuth.ts
export const useFirebaseAuth = () => { const [user, setUser] = useState<User | null>(); useEffect(() => { const unsubscribe = firebase.auth().onAuthStateChanged(user => { if (user) { console.info(`firebase: authorized (uid: ${user.uid})`); const userName = user.displayName ? user.displayName : 'åç¡ã'; setUser({ uid: user.uid, name: userName }); } else { console.info(`firebase: unauthorized`); setUser(null); } }); return () => { console.info(`firebase: unsubscribe onAuthStateChanged`); unsubscribe(); }; }, []); return user; };
useEffectã使ã£ã¦onAuthStateChangedã®subscribeãéå§ãcomponentãç ´æ£ãããã¨ãã«unsbscribeããã¨ããæãã«ãªãã¾ãããcontextã¯ããã ãã«ãªãã¾ããGatsbyã¯gatsby-browserã¨gatsby-ssrã®wrapRootElementã§ãã®Probiderã§å ãã§ãããã°OKã§ãã
FirebaseAuthProvider.tsx
export const FirebaseAuthContext = React.createContext<{ user?: User | null; }>({}); const FirebaseAuthProvider: React.FC<{}> = ({ children }) => ( <FirebaseAuthContext.Provider value={{ user: useFirebaseAuth() }}> {children} </FirebaseAuthContext.Provider> );
ããã§èªè¨¼ç¶æ ãå¤ãã£ãã¿ã¤ãã³ã°ã§contextã®userãæ´æ°ããã¦åæç»ãèµ°ãããã«ãªãã¾ãããcontextã®æ å ±ã¯useContextã使ãã°åç §ã§ãã¾ãã
Firebase.firestore
ä»åã¯ããããTodoã¢ããªãä½ãã®ã§ã¦ã¼ã¶ã¼ãã¨ã®Todoãªã¹ããFirestoreã«ä¿åãã¾ããæ§æã¯ç´ æ´ã«Usersã¨ããCorrectionã®ä¸ã«Useråä½ã§docmentãä½ãããã®ä¸ã«SubCorrectionã§todosãä¿åãã¾ãã
FirestoreãonSnapshot
ã§Observerãç»é²ããå®è£
ã«ãªãï¼ãªã¢ã«ã¿ã¤ã åæãå¿
è¦ãªããã°ãããªããã©ï¼ã®ã§ãåæ§ã«useEffectã使ã£ãcustom hookãä½ãã¾ãã
useFirestoreTodos.ts
export const useFirestoreTodos = (uid: string, filter: VisibilityFilter) => { const [todos, setTodos] = useState<Todo[]>(); useEffect(() => { const collection = todosCollection(uid); let query: firebase.firestore.Query; switch (filter) { case SHOW_ACTIVE: query = collection .where(`completed`, `==`, false) .orderBy(`createdAt`, `desc`); break; case SHOW_COMPLETED: query = collection .where(`completed`, `==`, true) .orderBy(`createdAt`, `desc`); break; default: query = collection.orderBy(`createdAt`, `desc`); break; } const unsubscribe = query.onSnapshot(snapshot => { console.info(`firestore: receive todos: size=${snapshot.docs.length}`); const todos = snapshot.docs.map(doc => toModel(doc.id, doc.data())); setTodos(todos); }); return () => { console.info(`firestore: unsubscribe onSnapshot:todos`); unsubscribe(); }; }, [filter]); return todos; };
authã¨åæ§ã§ããããªã¹ãã®æ¤ç´¢æ¡ä»¶ã¯å¤æ´åºæ¥ãã®ã§ãuseEffectã®ç¬¬2å¼æ°ã«filterãæå®ãã¦filterãå¤æ´ããããobserverãç»é²ãç´ãããã«ãã¾ããå®éã«ãã®hookã使ã£ãcomponentã¯ãããªå½¢ã«ãªãã¾ãã
TodoContents.tsc
const TodoContents = ({ user }: Props) => { const [filter, setFilter] = useState<VisibilityFilter>(SHOW_ALL); const todos = useFirestoreTodos(user.uid, filter); const handleAddTodoSubmit = async (todoName: string) => { await addTodoAction(user.uid, todoName); }; const handleClickTodo = async (todo: Todo) => { await updateTodoAction(user.uid, todo.id, !todo.completed); }; const handleClickDeleteTodo = async (todo: Todo) => { await deleteTodoAction(user.uid, todo.id); }; return ( <section> {todos ? ( <> <AddTodoForm onSubmit={handleAddTodoSubmit} /> <TodoList todos={todos} onClickTodo={handleClickTodo} onClickDeleteTodo={handleClickDeleteTodo} /> <TodoFilter currentFilter={filter} onClick={setFilter} /> </> ) : ( <div>ãã¼ã§ããã</div> )} </section> ); };
åããã¦ã¿ãã¨ãããªæã
ããã¾ã
- ç°¡åã«ãã触ã£ã¦ããªããã©Firestoreã¾ããã®è¨è¨ããã¢ã«ãªããªã¼ã¨ããå°è±¡ã§ããRDBã§ã¯ââã§ããã®ã«ã¨ãæããã«ï¼ããããå ¨ç¶å¥ç©ã ãã©ï¼ãFirestoreã«æé©åããè¨è¨ãèãã¦ããªãã¨ãããã§ããã
- ãã¨ã¯CloudFunctionããããç´ æ¯ãããã°ãã ããããµã¼ãã¹ä½ãã®ã«å¿ è¦ãªãã®ã¯äºè¶³ãããã
- docmentæ´æ°ããã¨ãã«onSnapshotã§2åéç¥ãæµãã¦ããã®ãã¡ãã£ã¨è¬æåã§ãããserverTimestampã®ä»æ§ã£ã½ããã©è¦èª¿æ»ã
- ãã¹ãæ¸ãã®ã¯ã©ãããã®ãããã£ã¦ãªãã®ã§å¾ã»ã©
é¢é£
ã¨ãããããªãã¨ã試ãã¦ããã¨ã³ã¸ãã¢HUBã§æ°åã®å ¥ã£ãå ¥éè¨äºãæµãã¦ããã®ã§ç´¹ä»ãªã©