FOREWORD / DESCRIPTION
I am trying to use React\'s new hooks feature for an e-commerce website that I am building, and have been having an issue wor
Seperate the Reducer from the functional component that helped me solve mine
An example based on Ryans excellent answer.
const memoizedReducer = React.useCallback((state, action) => {
switch (action.type) {
case "addRow":
return [...state, 1];
case "deleteRow":
return [];
default:
throw new Error();
}
}, []) // <--- if you have vars/deps inside the reducer that changes, they need to go here
const [data, dispatch] = React.useReducer(memoizedReducer, _data);
As you indicated, the cause is the same as the related answer of mine that you linked to. You are re-creating your reducer whenever Provider
is re-rendered, so in some cases React will execute the reducer in order to determine whether or not it needs to re-render Provider
and if it does need to re-render it will detect that the reducer is changed, so React needs to execute the new reducer and use the new state produced by it rather than what was returned by the previous version of the reducer.
When you can't just move the reducer out of your function component due to dependencies on props or context or other state, the solution is to memoize your reducer using useCallback, so that you only create a new reducer when its dependencies change (e.g. productsList
in your case).
The other thing to keep in mind is that you shouldn't worry too much about your reducer executing twice for a single dispatch. The assumption React is making is that reducers are generally going to be fast enough (they can't do anything with side effects, make API calls, etc.) that it is worth the risk of needing to re-execute them in certain scenarios in order to try to avoid unnecessary re-renders (which could be much more expensive than the reducer if there is a large element hierarchy underneath the element with the reducer).
Here's a modified version of Provider
using useCallback
:
const Context = React.createContext();
const Provider = props => {
const memoizedReducer = React.useCallback(createReducer(productsList), [productsList])
const [state, dispatch] = React.useReducer(memoizedReducer, []);
return (
<Context.Provider value={{ state, dispatch }}>
{props.children}
</Context.Provider>
);
}
Here is a modified version of your codepen: https://codepen.io/anon/pen/xBdVMp?editors=0011
Here are a couple answers related to useCallback
that might be helpful if you aren't familiar with how to use this hook: