If we look at the standard example of increment, decrement with useReducer hook:
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>
</>
);
}
As I understand it, the useReducer function in the above code has bound 'dispatch' to the 'reducer' function so when we call dispatch it executes the reducer function. However, looking at the code the reducer function expects 2 params, however we only pass it one param when we execute dispatch - but it works. Why is this?.. many thanks.
CodePudding user response:
the arguments in reducer
are handled by the useReducer
hook automatically
the dispatch
method in useReducer
all it does is update the state with the appropriate action object in your case decrement
or increment
. So
when dispatch
call reducer
the arguments in reducer
are handled by the useReducer hook automatically : the state is known, and the action is just the argument of dispatch which is passed along to the reducer as its second argument.
CodePudding user response:
The dispatch()
function is a child function returned by the useReducer()
. This gives it scoped access to the reducer that you passed into the useReducer()
.
So when you call the dispatch()
like so dispatch({type: 'decrement'})
the dispatch()
will take the action that you passed to it and grab the reducer that you passed into the useReducer()
and it will pass in the state and the action that you just passed into dispatch()
to your reducer to get the desired state and use that value to set the state and re render the component.
So think of the useReducer()
function something like the following:
const useReducer = (reducer, initialState) => {
// react stuff being done here connect rendering and state
// but for this example I will just hardcode the state to the initial state
const state = initialState
const dispatch = (action) => {
// react will set the state with your reducer and re render the component
React.dispatchState(reducer(state, action))
}
return [state, dispatch]
}
IMPORTANT: this is by no way the actual code react uses as the useReducer()
but it will give you a good representation of how the code is working. You can see the source code here https://github.com/facebook/react/blob/main/packages/react/src/ReactHooks.js and follow the bread crumbs to see how react actually does it.
CodePudding user response:
The reducer
function and the dispatch
function are not the same. Here's an extremely simplified example that might help you think conceptually about the data flow.
Note that this is neither technically accurate nor really how the
useReducer
hook works. If you want to review the actual source code, here's the entrypoint for examination: the line whereuseReducer
is exported from the codebase in the18.2.0
release: https://github.com/facebook/react/blob/v18.2.0/packages/react/src/ReactHooks.js#L87
let state;
function useReducer (reducer, intialState) {
state = intialState;
const dispatch = (action) => {
const next = reducer(state, action);
const changed = !Object.is(state, next);
state = next;
if (changed) {
// Causes React to re-render (code not shown)
}
};
return [state, dispatch];
}