Home > Back-end >  Setting Initial React Context State from Apollo Query
Setting Initial React Context State from Apollo Query

Time:10-30

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>
    );
};
  • Related