Reducer
// src/reducers/FooReducer.js
export function FooReducer(state, action) {
switch (action.type) {
case 'update': {
return action.newState;
}
// ... other actions
default:
throw new Error('Unknown action type');
}
}
Component
// src/components/BarComponent.js
export function BarComponent() {
const [state, dispatch] = useReducer(FooReducer, []);
return (
{state.map((item) => (<div />))}
);
}
Test
// src/components/BarComponent.test.js
it('should render as many divs as there are items', () => {
act(() => {
const { result } = renderHook(() => useReducer(FooReducer, [1]));
const [, dispatch] = result.current;
wrapper = mount(<BarComponent />);
dispatch({type: 'update', newState: [1, 2, 3]});
});
expect(wrapper.find(div)).toHaveLength(3);
});
The above test example does not work, but serves to demonstrate what I am trying to achieve. and would actually render 0 div, as the initial state declared in the component contains 0 items.
How would I go about modifying a reducer's state or changing the initialState it is deployed with for testing purposes?
I am used to Redux reducers being used throughout multiple components, but useReducer needs a passed initialState... which raises the question: Is react-hook's reducer usable through multiple components as a single instance or will it always be 2 separate instances?
In your example, you're trying to test two things at the same time, which would be better off as separate tests: A unit test for your reducer, and a component test where the component uses the reducer.
Similar to a Redux reducers, your reducer is easily unit testable since you're exporting it as a pure function. Just pass in your initial state into the
state
argument, and your action intoaction
:You could also test it in the context of
useReducer
if you prefer:Here's how
useReducer
is different from Redux: You can reuse the reducer itself, but if you have multipleuseReducer
s, thestate
anddispatch
returned from each one, as well as the initial state, will be separate instances.In order to test that your BarComponent updates when the reducer updates, you'll need a way to trigger
dispatch
from within the component, since you're callinguseReducer
inside your component. Here's an example:This probably isn't very realistic, since I'm hardcoding the new array in the component itself, but hopefully it gives you the idea!