Search code examples
reactjsroutesstoreuse-effect

How to force useEffect in app.js to load before other component's useEffect when refreshing page manually


I'm stuck with something and can't seem to find solution for my problem. The thing is when I manually type let's say you target localhost:3000/dashboard it should trigger app.js which is my root component and dashboard component. The problem here is that dashboard component needs user from store which it can get only in app.js useEffect hook. To simplify this approach, problem is that dashboard useEffect triggers before app.js useEffect but I want to make it the opposite way otherwise dashboard useEffect will return undefined user from the store, but user should be either null or real user name.

Here is my simplified code for a better understanding:

App.js -> sets user in my store and has routes

function App() {

const [{ user }, dispatch] = useStateValue();

useEffect(() =>{
const unsubscribe = auth.onAuthStateChanged((authUser) => {
  if (authUser) {
    dispatch({
      type: 'SET_USER',
      user: authUser
    });
  } else {
    dispatch({
      type: 'SET_USER',
      user: null
    });
  }
});

return () => {
  unsubscribe();
}

}, []);

console.log('User is >>>', user);

return (
<Router>
  <div className="app">
    <Switch>

      <Route path="/dashboard">
        <Dashboard />
      </Route>

      <Route path="/" >
        <Header />
        <Home />
      </Route>
      
    </Switch>
  </div>
</Router>
);

##############################

Dashboard.js -> should obtain user from the store so it can fetch other data but error happens cuz user is undefined because Dashboard useEffect happens before app.js useEffect

const Dashboard = () => {

const [{user}] = useStateValue();
useEffect(() => {
    console.log(`user is ${user}`);
    getItemsByUser(user);
}, [])

const getItemsByUser = async (user) => {
    const items = await firebaseService.getUserProducts(user);
    console.log(items[0])
}
}

The main problem here is that Dashboard useEffect triggers before App.js useEffect when I manually refresh the page. If I want to navigate to /dashboard within my header/navigation anything should be fine, but I want to cover manually approach. How can I solve this?..maybe middleware of any kind should help but I can't find anything usefull at all.


Solution

  • Program your effect to be reactive.

    useEffect(() => {
      // This effect now handles all possible values of user.
      // It will also update the Dashboard whenever the user state changes.
      // This is the general approach because react state/context state
      // value updates are asynchronous so consumers must
      // handle undefined state.
      if (user) getItemsByUser(user);
    }, [user]);
    

    Btw, because of the asynchronous nature of React, it's generally not helpful to think of solutions in terms of strict ordering (like trying to get one thing to happen before another). Instead, the logic in components and hooks should serve only to consume the available props/state and generate the right UI. Ie, they should be reactive. Effects are just a simple way to declare which functions should run when certain dependencies of the component are updated.