I'm using React.js, specifically Next.js, and I have a global React Context for the user. The context itself looks like:
const UserContext = createContext<{
user: User | undefined;
setUser: (u: User | undefined) => void;
}>({
user: undefined,
setUser: () => {},
});
export const UserProvider = ({ children }: { children: JSX.Element }) => {
const [user, setUser] = useState<User | undefined>(undefined);
return (
<UserContext.Provider value={{ user: user, setUser: setUser }}>
{children}
</UserContext.Provider>
);
};
export const useUser = () => React.useContext(UserContext);
And I am establishing it in my _app.tsx file like so:
<UserProvider>
<Component {...pageProps} />
</UserProvider>
However, what I would like is for on the initial page load for the website to use localstorage to get an accessToken and then query the server using Apollo Graphql for the user if they are already signed in. To accomplish this I tried add the following to my context provider:
// in User Provider
...
const { data, loading, error } = useQuery<{ me: UserResponse }>(MeQuery);
const initialUser = data?.me.user;
const [user, setUser] = useState<User | undefined>(initialUser);
...
This is not working as intended. When I log the outputs of this snippet, it prints everything twice, once as undefined, and then once with the values of the result of the query. However the second time around it is not being saved to the state variable so when I access UserContext
from other components, it says User
is undefined. How can I save the second render result with the actual data from the server in my state variable?
CodePudding user response:
To keep the user
value in sync with data
, you should call setUser(data)
inside a useEffect
when the data value changes.
export const UserProvider = ({ children }: { children: JSX.Element }) => {
const [user, setUser] = useState<User | undefined>(undefined);
const { data, loading, error } = useQuery<{ me: UserResponse }>(MeQuery);
useEffect(() => {
setUser(data);
}, [data]);
return (
<UserContext.Provider value={{ user: user, setUser: setUser }}>
{children}
</UserContext.Provider>
);
};