React function components may dispatch Redux actions using react-redux
's useDispatch
hook.
Consider this Redux state shape containing a clicked
of type boolean
:
const initialState = {
interactions: {
clicked: false,
},
};
Your state code might declare a markClicked
action:
const MARK_CLICKED = "MARK_CLICKED";
const markClicked = () => ({ type: MARK_CLICKED });
...and some kind of reducer to set clicked
to true
upon the markClicked
action:
switch (action.type) {
case MARK_CLICKED:
return {
...state,
interactions: {
...state.interactions,
clicked: true,
},
};
}
Your view code might dispatch an action to set clicked
to true
:
const Clicker = () => {
const dispatch = useDispatch();
return (
<button onClick={() => dispatch(markClicked())} type="button">
Click me!
</button>
);
};
You could set up Redux state with a full state tree:
const store = configureStore()({
interactions: {
clicked: false,
},
});
const view = render(
<Provider store={store}>
<Clicker />
</Provider>,
);
act(() => {
fireEvent.click(view.getByRole("button"));
});
expect(store.actions()).toEqual([markClicked()]);
So much work! That testing setup's complexity and resultant pain can easily grow with your application. You're also relying a bunch of Redux state in order to test your React view. It can be useful to separate view and state logic in tests: especially when state is shared across many places.
Instead, mock-react-redux
can completely replace any Redux interactions.
It'll instead swap out the dispatch
function with a Jest mock:
const { dispatch } = mockReactRedux();
const view = render(<Clicker />);
act(() => {
fireEvent.click(view.getByRole("button"));
});
expect(dispatch).toHaveBeenCalledWith(markClicked());
Much cleaner!
The dispatch
returned to your views is directly the result of calling jest.fn()
.
It'll receive any actions passed to it as normal function parameters.
If you'd like to execute some logic when actions are dispatched, you can use Jest's built-in logic for mock implementations:
dispatch.mockImplementation((action) => {
console.log("Received", action);
});