如何使用 useReducer 钩子测试组件?
How test a component using the useReducer hook?
减速机
// 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');
}
}
组件
// src/components/BarComponent.js
export function BarComponent() {
const [state, dispatch] = useReducer(FooReducer, []);
return (
{state.map((item) => (<div />))}
);
}
测试
// 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);
});
上面的测试示例不起作用,但可以用来演示我正在努力实现的目标。并且实际上会呈现 0 div,因为组件中声明的初始状态包含 0 个项目。
我将如何修改 reducer 的状态或更改它部署时用于测试目的的 initialState?
我已经习惯了在多个组件中使用 Redux reducer,但是 useReducer 需要一个传递的 initialState... 这引发了一个问题:React-hook 的 reducer 是否可以通过多个组件作为单个实例使用,或者它会始终是 2 个单独的实例吗?
在您的示例中,您正在尝试同时测试两件事,这最好作为单独的测试进行:针对您的减速器的单元测试,以及组件使用减速器的组件测试。
- How would I go about modifying a reducer's state or changing the initialState it is deployed with for testing purposes?
与 Redux reducer 类似,您的 reducer 很容易进行单元测试,因为您将其导出为纯函数。只需将您的初始状态传递给 state
参数,然后将您的操作传递给 action
:
it('returns new state for "update" type', () => {
const initialState = [1];
const updateAction = {type: 'update', newState: [1, 2, 3] };
const updatedState = fooReducer(initialState, udpateAction);
expect(updatedState).toEqual([1, 2, 3]);
});
如果您愿意,也可以在 useReducer
的上下文中进行测试:
it('should render as many divs as there are items', () => {
act(() => {
const { result } = renderHook(() => useReducer(FooReducer, [1]));
const [state, dispatch] = result.current;
dispatch({type: 'update', newState: [1, 2, 3]});
});
expect(state).toEqual([1, 2, 3]);
// or expect(state).toHaveLenth(3) if you prefer
});
- 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 asa single instance or will it always be 2 separate instances?
以下是 useReducer
与 Redux 的不同之处:您可以重用 reducer 本身,但是如果您有多个 useReducer
,则 state
和 dispatch
会从每一个,以及初始状态,都是单独的实例。
为了测试 BarComponent 在 reducer 更新时是否更新,您需要一种从组件内部触发 dispatch
的方法,因为您是在组件内部调用 useReducer
。这是一个例子:
export function BarComponent() {
const [state, dispatch] = useReducer(FooReducer, []);
const handleUpdate = () => dispatch({type: 'update', newState: [1, 2, 3]})
return (
<>
{state.map((item) => (<div key={item} />))}
<button onClick={handleUpdate}>Click to update state</button>
</>
);
}
it('should render as many divs as there are items', () => {
wrapper = mount(<BarComponent />);
expect(wrapper.find('div')).toHaveLength(0);
wrapper.find('button').simulate('click');
expect(wrapper.find('div')).toHaveLength(3);
});
这可能不太现实,因为我在组件本身中对新数组进行了硬编码,但希望它能给您带来灵感!
减速机
// 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');
}
}
组件
// src/components/BarComponent.js
export function BarComponent() {
const [state, dispatch] = useReducer(FooReducer, []);
return (
{state.map((item) => (<div />))}
);
}
测试
// 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);
});
上面的测试示例不起作用,但可以用来演示我正在努力实现的目标。并且实际上会呈现 0 div,因为组件中声明的初始状态包含 0 个项目。
我将如何修改 reducer 的状态或更改它部署时用于测试目的的 initialState?
我已经习惯了在多个组件中使用 Redux reducer,但是 useReducer 需要一个传递的 initialState... 这引发了一个问题:React-hook 的 reducer 是否可以通过多个组件作为单个实例使用,或者它会始终是 2 个单独的实例吗?
在您的示例中,您正在尝试同时测试两件事,这最好作为单独的测试进行:针对您的减速器的单元测试,以及组件使用减速器的组件测试。
- How would I go about modifying a reducer's state or changing the initialState it is deployed with for testing purposes?
与 Redux reducer 类似,您的 reducer 很容易进行单元测试,因为您将其导出为纯函数。只需将您的初始状态传递给 state
参数,然后将您的操作传递给 action
:
it('returns new state for "update" type', () => {
const initialState = [1];
const updateAction = {type: 'update', newState: [1, 2, 3] };
const updatedState = fooReducer(initialState, udpateAction);
expect(updatedState).toEqual([1, 2, 3]);
});
如果您愿意,也可以在 useReducer
的上下文中进行测试:
it('should render as many divs as there are items', () => {
act(() => {
const { result } = renderHook(() => useReducer(FooReducer, [1]));
const [state, dispatch] = result.current;
dispatch({type: 'update', newState: [1, 2, 3]});
});
expect(state).toEqual([1, 2, 3]);
// or expect(state).toHaveLenth(3) if you prefer
});
- 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 asa single instance or will it always be 2 separate instances?
以下是 useReducer
与 Redux 的不同之处:您可以重用 reducer 本身,但是如果您有多个 useReducer
,则 state
和 dispatch
会从每一个,以及初始状态,都是单独的实例。
为了测试 BarComponent 在 reducer 更新时是否更新,您需要一种从组件内部触发 dispatch
的方法,因为您是在组件内部调用 useReducer
。这是一个例子:
export function BarComponent() {
const [state, dispatch] = useReducer(FooReducer, []);
const handleUpdate = () => dispatch({type: 'update', newState: [1, 2, 3]})
return (
<>
{state.map((item) => (<div key={item} />))}
<button onClick={handleUpdate}>Click to update state</button>
</>
);
}
it('should render as many divs as there are items', () => {
wrapper = mount(<BarComponent />);
expect(wrapper.find('div')).toHaveLength(0);
wrapper.find('button').simulate('click');
expect(wrapper.find('div')).toHaveLength(3);
});
这可能不太现实,因为我在组件本身中对新数组进行了硬编码,但希望它能给您带来灵感!