reactjsapollo

Fetch user only once when filling react global state


I am using the following code to manage logged in user fetched from apollo query in my React app.

import { useQuery } from '@apollo/client';
import { createContext, useEffect, useState } from 'react';
import { GET_USER } from './queries';

// Create a Context for logged in user (global state)
export const AuthContext = createContext();

function AuthProvider({ children }) {
  const { loading, error, data } = useQuery(GET_USER);
  const [user, setUser] = useState(null); // Global state

  useEffect(() => {
    if (!loading && !error && data.userInfo != null) {
      setUser(data.userInfo);
    }
  }, [loading, error, data])

  return (
    <AuthContext.Provider value={{ user, setUser }}>
      {children}
    </AuthContext.Provider>
  );
}

export default AuthProvider;

App.js:

function App() {
  return (
    <div className="App">
      <AuthProvider>
        <NavigationBar />
          <div className="main m-4">
            <BrowserRouter>
              ...
            </BrowserRouter>
          </div>
        <Footer />
      </AuthProvider>
    </div>
  );
}

The problem is that the query is executed every time I re-render any component so it keeps "flashing". I would like the query to only happen once in the beginning. Any ideas how to do that?


Solution

  • Try moving the useQuery call inside a child component that only mounts once, not directly in AuthProvider.

    function AuthProvider({ children }) {
      const [user, setUser] = useState(null);
    
      return (
        <AuthContext.Provider value={{ user, setUser }}>
          <UserFetcher />
          {children}
        </AuthContext.Provider>
      );
    }
    
    function UserFetcher() {
      const { loading, error, data } = useQuery(GET_USER);
      const { setUser } = useContext(AuthContext);
    
      useEffect(() => {
        if (!loading && !error && data?.userInfo) {
          setUser(data.userInfo);
        }
      }, [loading, error, data]);
    
      return null;
    }
    

    This should ensure the query runs once on mount.