Hooked on hooks: how to use React’s useReducer()
So the React Conference just happened and as always something new happened. Hooks happened! The React team talked about suspense, lazy loading, concurrent rendering, and hooks :D.
Now I’ll talk about my favorite hook useReducer
and how you use it.
In my PokemonInfo
component, I have:
const [{ count, loading }, dispatch] = useReducer(reducer, initialState);
Which is equivalent to:
const [state, dispatch] = useReducer(reducer, initialState);
const { count, loading } = state;
So what is const [state, dispatch] = useReducer(param1, param2)
Let’s first talk about array destructuring which is happening below.
const [state, dispatch] = useReducer(initialState);
Here’s an example of array destructing:
let myHeroes = ['Ant man', 'Batman']; // Mixing DC & Marvel :D
let [marvelHero, dcHero] = myHeroes; // destructuring array
/**
* myHeroes[0] == marvelHero => is 'Ant man'
* myHeroes[1] == dcHero => is 'Batman'
*/
So the method useReducer
has two items in its array state
and dispatch
. Also the useReducer
takes in two parameters: one is reducer
the other is initial-state
.
In the useReducer
param reducer
, I pass in:
What this does is it takes in two arguments. One is the current state of the reducer and the other is the action. The action.type
decides how it will update the reducer and return a new state to us.
So if the action.type === increment
case 'increment': {
return { ...state, count: state.count + 1, loading: false };
}
…it will return the state, which will have its count updated to +1 and loading set to false. Also where it says state.count + 1
here the state
is actually the previous state.
In useReducer
param initialState
I pass in:
const initialState = {
loading: false,
count: 0
};
So if this is the initial state, the useReducer
method returns two items from its array, state
and dispatch
. The state
method is an object which has two keys count & loading
that I destructure in my destructured array.
So I destructure an array, and inside the array, I destructure an object on the first index of the array like below.
const [{ count, loading }, dispatch] = useReducer(reducer, initialState);
Also I have a method called delay
// return true after 1500ms in time argument is passed to.
const delay = (time = 1500) => {
return new Promise(resolve => {
setTimeout(() => {
resolve(true);
}, time);
});
};
Now in my render method when I click the +
button
<button type="button" onClick={onHandleIncrement}>+</button>
the onHandleIncrement
function is executed, which does the following:
const onHandleIncrement = async () => {
dispatch({ type: 'loading' });
await delay(500);
dispatch({ type: 'increment' });
};
It initially sets loading
to true, adds a delay of 500ms
and then increments the counter. Now I know this is not a real world case example, but it explains the point as to how a reducer works.
Last thing:
<p>Count {loading ? 'loading..' : count}</p>
If loading
is true, I show Count loading..
else I show Count {value}
.
This is how it looks in the UI:
I tried replicating Dan Abramov’s code that he showcased at the React Conference 2018. Here is the link to the code repository. Enjoy. :)
Kindly do note that hooks are in an alpha version of React, and are in no way advised to be used in production. But there is a strong possibility that they will become a huge part of the eco-system in the future. So you should start playing around with react hooks now.