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.