3.2 KiB
categories | tags | ||||
---|---|---|---|---|---|
|
|
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 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
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 theuseState
hook. The functional role is the same, it is just that the reducer offers more than one type of update.
- A pure function that accepts two parameters:
Example reducer
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:
{
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:
dispatch({ type: "increase" });
To view the updated value:
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:
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:
dispatch(
{
type: 'increase_by_payload'
payload: 3,
});
Then we would update our reducer to handle this case:
function reducer(state, action) {
switch (action.type) {
...
case 'increase_by_payload':
return {...state, counter: state.counter + action.payload}
default:
throw new Error();
}
}