Created
July 19, 2021 04:32
-
-
Save samselikoff/ac8076c6c224786da23c9297567585cf to your computer and use it in GitHub Desktop.
Example useAuth hook using Zustand, SWR and Suspense
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import firebase from "firebase/app"; | |
import "firebase/auth"; | |
import { gql, GraphQLClient } from "graphql-request"; | |
import { SWRConfig } from "swr"; | |
import create from "zustand"; | |
import { computed } from "zustand-middleware-computed-state"; | |
const firebaseConfig = { | |
// | |
}; | |
let firebaseApp = !firebase.apps.length | |
? firebase.initializeApp(firebaseConfig) | |
: firebase.app(); | |
let auth = firebaseApp.auth(); | |
const client = new GraphQLClient( | |
"https://fitness-challenge-backend.herokuapp.com/v1/graphql", | |
{ headers: {} } | |
); | |
let firebaseUser; | |
let promise = new Promise((r) => (resolve = r)); | |
let resolve; | |
export const useStore = create( | |
computed( | |
(set, get) => ({ | |
currentUser: undefined, | |
getSessionToken: () => firebaseUser.getIdToken(), | |
login: (email, password) => | |
auth.signInWithEmailAndPassword(email, password), | |
logout: () => auth.signOut(), | |
}), | |
(state) => { | |
let status = | |
state.currentUser === undefined | |
? "unknown" | |
: state.currentUser === null | |
? "anonymous" | |
: "authenticated"; | |
return { | |
status, | |
}; | |
} | |
) | |
); | |
auth.onAuthStateChanged(async (f) => { | |
firebaseUser = f; | |
if (firebaseUser) { | |
let res = await fetcher( | |
currentUserQuery, | |
JSON.stringify({ id: firebaseUser.uid }) | |
); | |
useStore.setState({ | |
currentUser: res.users_by_pk, | |
}); | |
} else { | |
useStore.setState({ currentUser: null }); | |
} | |
resolve(); | |
}); | |
let fetcher = async (query, variables = "{}") => { | |
if (firebaseUser) { | |
let token = await firebaseUser.getIdToken(); | |
client.setHeader("authorization", token); | |
} | |
return client.request(query, JSON.parse(variables)); | |
}; | |
export default function AuthedSWRProvider({ children }) { | |
let status = useStore((state) => state.status); | |
if (status === "unknown") { | |
throw promise; | |
} | |
return <SWRConfig value={{ fetcher }}>{children}</SWRConfig>; | |
} | |
let currentUserQuery = gql` | |
query CurrentUser($id: String!) { | |
users_by_pk(id: $id) { | |
id | |
name | |
} | |
} | |
`; |
The thrown promise is to integrate with Suspense. So if a component needs currentUser and it hasn't been loaded once yet, that component will suspend, so a parent will render its fallback.
Not sure if it needs to be updated but the ideas should still be relevant!
https://stackblitz.com/github/vaynevayne/react-auth?file=src%2Frouters.tsx%3AL37
hello, I refer to your idea to build a template project, but there is a problem that the redirect in the rootLoader will not execute. Could you please help me see?
Don't have time to look into this in detail unfortunately! Your best bet is asking on a Discord server or a relevant GitHub Issues forum.
ok thanks
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi, before CurrentUser loaded, does the returned promise prevent the whole application from executing down?
Is there a bug? Does getCurrentUser not execute when you refresh the page again?
Also, to this day, does this code need to be updated? Like, it's got some good ideas, etc.
If you have time, can you provide an online demo? Thanks for your time.