J.D.1731
J.D.1731

Reputation: 415

Update global state after RTK Query loads data

I've noticed a problem with splitting responsibilities in React components based on the fetched data using RTK Query.

Basically, I have two components like HomePage and NavigationComponent. On HomePage I'd like to fetch the information about the user so that I can modify NavigationComponent accordingly.

What I do inside HomePage:

import { setNavigationMode } from "features/nav/navSlice";

export default function HomePage() {
  const {data: user} = useGetUserDataQuery();
  const dispatch = useAppDispatch();
  const navMode = user ? "all-options" : "none";

  dispatch(setNavigationMode(navMode)); // here I change the default Navigation mode

  return <MainLayout>
    <Navigation/>
    <Content/>
    <Footer/>
  </MainLayout>;
}

The HomePage is a special Page when the NavigationComponent shouldn't display any options for the not logged-in user. Other pages present additional Logo and Title on Nav.

React communicates:

Warning: Cannot update a component (NavComponent) while rendering a different component (HomePage). To locate the bad setState() call inside HomePage, follow the stack trace as described in https://reactjs.org/link/setstate-in-render

Not sure what is the right way to follow. Whether the state should be changed in the GetUser query after it is loaded - that doesn't seem to be legit.

Upvotes: 2

Views: 3474

Answers (2)

J.D.1731
J.D.1731

Reputation: 415

The solution was too obvious. The dispatch should be run in useEffect.

import { setNavigationMode } from "features/nav/navSlice";

export default function HomePage() {
  const {data: user} = useGetUserDataQuery();
  const dispatch = useAppDispatch();
  const navMode = user ? "all-options" : "none";

  // changed lines
  useEffect( () => {
    dispatch(setNavMode(navMode));
  }, [navMode, dispatch]);
  // /changed lines

  return <MainLayout>
    <Navigation/>
    <Content/>
    <Footer/>
  </MainLayout>;
}

Thank you @papa-xvii for the hint with changing the navMode after user login. That solves the second problem I had. However I cannot accept the answer as it does not solve the problem I described above.

Upvotes: 2

Abdul Mahamaliyev
Abdul Mahamaliyev

Reputation: 866

problem is dispatch calls every render. Instead you can create a navigationSlice (if you don't have already) and use extraReducers for matching your authorization action like:

extraReducers: (builder) => {
    builder.addMatcher(
      usersApi.endpoints.login.matchFulfilled,
      (state, { payload }) => {
        if (payload.user) {              
          state.navigationMode = "all-options"
        }
      }
    );
  }

This way, state.navigationMode will only change when authorization changes

Upvotes: 2

Related Questions