--- tags: - javascript - react - react-hooks --- # `useReducer` The `useReducer` hook is best used in scenarios where you are manipulating state in a way that is too complex for the trivial [useState](useState.md) use case. `useState` is best employed when you are updating a single value or toggling a boolean. If you are updating the state of an object or more complex data structure, it is often more efficient to employ `useReducer`. This makes the code more manageable and also helps with separating state management from rendering. ## Syntax ```jsx const [state, dispatch] = useReducer(reducer, initialState); ``` - `initialState` - The starting state, typically an object - `reducer` - A pure function that accepts two parameters: - The current state - An action object - The reducer function must update the current state (immutably) and return the new state - We can think of the reducer as working in the same manner as `state`/`setState` in the `useState` hook. The functional role is the same, it is just that the reducer offers more than one type of update. ### Example reducer ```js function reducer(state, action) { let newState; switch (action.type) { case "increase": newState = { counter: state.counter + 1 }; break; case "descrease": newState = { counter: state.counter - 1 }; break; default: throw new Error(); } return newState; } ``` In this example we are updating an object with the following shape: ```js { counter: 0, } ``` This would be the `initialState` that we pass to the `useReducer` hook along with a reference to `reducer` above. To update the state we would invoke the `dispatch` function which applies one of the actions defined in the reducer. For example the following dispatch increments the counter by one: ```js dispatch({ type: "increase" }); ``` To view the updated value: ```js console.log(state.counter); ``` ### Refining the syntax Because React doesn't mutate state, the reducer doesn't directly modify the current state in the `state` variable, it creates a new instance of the state object on each update. In the reducer example above this is achieved by declaring a variable `newState` that is updated by each `action` type and then returned. There is a more elegant way of doing this using spread syntax: ```js function reducer(state, action) { switch (action.type) { case "increase": return { ...state, counter: state.counter + 1 }; break; case "decrease": return { ...state, counter: state.counter - 1 }; break; default: throw new Error(); } } ``` ### Including payloads In the examples so far, we have updated the the state directly via the action type however it is also possible to pass data along with the `action.type` as `action.payload`. For example: ```js dispatch( { type: 'increase_by_payload' payload: 3, }); ``` Then we would update our reducer to handle this case: ```js function reducer(state, action) { switch (action.type) { ... case 'increase_by_payload': return {...state, counter: state.counter + action.payload} default: throw new Error(); } } ```