Let’s say we had a basic reducer function like this:
function reducer(state, action) {
if(action.type === 'add')
return state + action.value;
if(action.type === 'multiply')
return state * action.value;
return state;
}
Is there a practical difference between using our reducer with useReducer
:
const [x, dispatch] = useReducer(reducer, 1);
dispatch({ type: 'add', value: 2 });
dispatch({ type: 'multiply', value: 10 });
Or using our reducer with useState
:
const [x, setX] = useState(1);
setX(x => reducer(x, { type: 'add', value: 2 }));
setX(x => reducer(x, { type: 'multiply', value: 10 }));
What I’m asking is whether or not there is a fundamental difference between how react handles these examples or if useReducer
is really just useState
with syntactic sugar.
2
Answers
The "practical difference" is code readability and maintainability.
How React handles
useState
anduseReducer
is indeed very similar, you could almost say identical (currently).Generally in React, which should be seen as a high level abstraction, you should just follow the rules and recommendations, and don’t ask for the implementation details. You might even willingly sacrifice some performance for cleaner code (in non-performance-critical situations).
I.e. two React mechanisms might work exactly the same or totally different, but that doesn’t matter, you are using React to write declarative code and commonly known patterns, and let React handle the implementation.
E.g. imagine
useState
oruseReducer
might be implemented in some way today, but tomorrow they change one of them fundamentally because they noticed they can optimize something significantly under the assumption that people mostly follow the recommendations. But the usage of both of them would stay the same, and you don’t have to care, React simply became a little bit faster as far as you are concerned.But of course, it is good to be curious:
Your assumption is correct,
useState
anduseReducer
do almost the same. As far as I know (not 100% sure),useState
is (or was) even usinguseReducer
internally. That meansuseState
would be syntactic sugar ofuseReducer
, not the other way around.Regarding performance:
Of course, one function declaration and/or function call more or less, as in your example, should theoretically make a difference regarding performance, but that should be negligible, or even not true in practice (due to compiler optimizations maybe. I don’t know). But that’s probably not the point of your question.
See also: Comparing useState and useReducer
While there are differences between the two, they’re mostly regarding how these two handle complex logic and performance. At the very core, however, they complete the same task.
We can examine this closer:
useReducer would be more beneficial to use for with a more complex state logic as it prevents the action of unnecessary re-renders. We can see this in the example you provided as the reducer function has been clearly separated away from its component, thus showing that the component is not responsible for updates regarding state logic.
useState, on the other hand, manually calls the reducer function and is also responsible for updates regarding state logic. This, despite being functional, can quickly begin to struggle with performance once the state logic becomes more complex.