Reputation: 415
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 insideHomePage
, 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
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
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