Home > Back-end >  How to migrate Redux to React hooks
How to migrate Redux to React hooks

Time:02-24

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/

  • Related