Reputation: 598
I'm currently trying to figure out how to avoid creating an infinite loop when wrapping my application in a Context provider (taking in values from useReducer) and then updating via child component with a useEffect hook.
There's an example of the problem here on CodeSandbox.
Obviously it's difficult to talk about the problem without reposting all the code here, but key points:
Root:
function App() {
const [state, dispatch] = useReducer(reducer, initialState);
const value = { state, dispatch };
return (
<Context.Provider value={value}>
...
</Context.Provider>
Child:
export const Page1: FC = () => {
const { dispatch, state } = useContext(Context);
const { isLoading } = state;
useEffect(() => {
dispatch({
type: "loading",
payload: false
});
}, [dispatch]);
return (...)
I'm probably missing something obvious, but any pointers might help others who run into the same problem.
Upvotes: 0
Views: 1519
Reputation: 18476
The root of the problem is here
<Route path="/page1" component={() => <Page1 />} />
When you pass inlined arrow function as component you basically creating new component for every render and forcing Route
to completely re-mount this part. When it happens useEffect
gets called again and so on, and so on.
You need to change it like that:
<Route path="/page1"><Page1 /></Route>
// or
<Route path="/page1" component={Page1} />
Citation from react-router docs:
When you use component (instead of render or children, below) the router uses React.createElement to create a new React element from the given component. That means if you provide an inline function to the component prop, you would create a new component every render. This results in the existing component unmounting and the new component mounting instead of just updating the existing component. When using an inline function for inline rendering, use the render or the children prop (below).
Source: https://reactrouter.com/web/api/Route/route-render-methods
Upvotes: 5