I am currently trying to migrate Redux files to React hooks (useReducer) with minimal changes. I am reusing Redux reducers and the initial states. e.g.
export const INITIAL_STATE = {
locale: "en"
};
export const reducer = (state = INITIAL_STATE, action) => {
switch (action.type) {
case "CHANGE_LOCALE":
return {
...state,
locale: action.payload
};
default:
return state;
}
};
I'd like to reuse Redux actions as well and need some help, currently, I have.
export const changeLocale = (payload) => {
return (dispatch) => {
Cookies.set("lc", payload);
dispatch({
type: "CHANGE_LOCALE",
payload
});
};
};
But dispatch here and dispatch in useReducer are different functions, what are the ways to migrate such action to be used in useReducer hook?
CodePudding user response:
You can't achieve this with plain a useReducer
though it's straight-forward to create a custom hook that can deal with the "thunk" (or whatever you want to call the actions which are functions) as well. Your base case reducer is:
const [ state, dispatch ] = React.useReducer(reducer, INITIAL_STATE);
However you need to modify your dispatch
to allow it to make it have "middleware" like behaviour:
const useReducerWithThunk = (reducer, initialState) => {
const [ state, innerDispatch ] = React.useReducer(reducer, initialState);
const myDispatch = async (action) => {
if (typeof action === 'function') {
// Wrap the result in a promise if it's not one already
return await Promise.resolve(action(myDispatch, () => state));
} else {
return innerDispatch(action);
}
};
return [ state, myDispatch ];
}
Then your code will do:
const [ state, dispatch ] = useReducerWithThunk(reducer, initialState);
This should be enough for a simple drop-in replacement of redux with thunks, however be mindful that dispatch
will become async which is not the default behaviour.
CodePudding user response:
useReducer is usually preferable to useState when you have complex state logic that involves multiple sub-values or when the next state depends on the previous one. useReducer also lets you optimize performance for components that trigger deep updates because you can pass dispatch down instead of callbacks.
Here’s the counter example from the useState section, rewritten to use a reducer:
const initialState = {count: 0};
function reducer(state, action) {
switch (action.type) {
case 'increment':
return {count: state.count 1};
case 'decrement':
return {count: state.count - 1};
default:
throw new Error();
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<>
Count: {state.count}
<button onClick={() => dispatch({type: 'decrement'})}>-</button>
<button onClick={() => dispatch({type: 'increment'})}> </button>
</>
);
}
so in your case
const initialState = { locale: "en"};
function reducer(state, action) {
switch (action.type) {
case "CHANGE_LOCALE":
Cookies.set("lc", payload);
return {
...state,
locale: action.payload
};
default:
return state;
}
}
function YourComponentName() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<>
locale: {state.locale}
<button onClick={() => dispatch({type: 'CHANGE_LOCALE'})}>update locale</button>
</>
);
}
reference : https://reactjs.org/docs/hooks-reference.html#usereducer
you can learn more here https://www.freecodecamp.org/news/how-to-convert-from-react-redux-classes-to-react-hooks-the-easy-way-eca2233e0e7a/