Skip to content

React Re-render on Context change.

Hi, I'm using a React Context to pass down authentication info. From what I understood, whenever the value provided by the Provider changes, every Consumers will be re-rendered. I just added an effect to my Root component, that executes only once (empty dependencies), whose goal is to check if the user is still connected.

// in root.js
// Do note that Root is a Consumer of AuthProvider 
const auth = useAuth(); // useContext(AuthenticationContext)
const [logged, setLogged] = useState(null)
  useEffect(() => {
      async initialPing() {
       await auth.getUser();
       setLogged(auth.isLogged());
      }
     initialPing();
    return () => {};
  }, []); // first time only

if (logged == null) return null;
// ...
// update navbar depending on the value of logged

Yet, the effect callback will update my Context's value but no re-render is triggered.

// in <AuthProvider>
const [identity, setIdentity] = React.useState({});

useEffect(() => { // debug purpose to log changes on identity
    console.log("provider: identity is", identity); // is called only once (startup) instead of twice
//--> means setIdentity() in responseInterceptor does nothing
    return () => {};
  }, [identity]);

// In use effect. On a succesful login, identity is correctly updated to the response data (univID)
const responseInterceptor = apiClient.interceptors.response.use((res) => { // handle response within 2xx
      let newIdentity = {};

      if ("univID" in res.data) newIdentity.univID = res.data.univID;
      if ("email" in res.data) newIdentity.email = res.data.email;
      if ("id" in res.data) newIdentity.id = res.data.id;

      setIdentity({ ...identity, ...newIdentity }); // update the identity -> not called 
      return res;
    },
    (err) => { // should handle any response whose status is not 2xx
      if (err.status == 304) {
        let newIdentity = {};

        if ("univID" in err.data) newIdentity.univID = err.data.univID;
        if ("email" in err.data) newIdentity.email = err.data.email;
        if ("id" in err.data) newIdentity.id = err.data.id;
  
        setIdentity({ ...identity, ...newIdentity });
        return err;
      }
      return err;
    });

  async function getUser() { // Called by component <Root>'s effect.
    try {
      const response = await apiClient.get("/api/users"); // BUG here
      return response.data;
    } catch (error) {
      const redirect = handleRedirect(error);
      if (redirect !== false) return redirect;

      debug("client:auth")("AuthProvider::get failed with", error);
      return error.response;
    }
  }

const context = {identity, login, logout, getUser, isLogged}

return <AuthContext.Provider value={context}>{children}</AuthContext.Provider>

Would you have any idea of what's going on ? I went through the docs of both axios and React, I cannot see where I did something wrong. The useEffect hook within Root is called, since I can see a 'GET /api/users' in the network tab. It's as if the responseInterceptor was not called for the Root's get. But this too is unreliable, as from time to time I manage to get the right behavior, but not always.

Edited by Benjamin Priour
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information