9

减速器

// 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 个项目。

  1. 为了测试目的,我将如何修改减速器的状态或更改它部署的初始状态?

  2. 我习惯于在多个组件中使用 Redux 减速器,但是 useReducer 需要一个传递的 initialState ......这引发了一个问题:react-hook 的减速器是否可以通过多个组件作为单个实例使用,还是始终是 2 个单独的实例?

4

1 回答 1

17

在您的示例中,您正在尝试同时测试两件事,这将作为单独的测试更好:对减速器的单元测试,以及组件使用减速器的组件测试。

  1. 为了测试目的,我将如何修改减速器的状态或更改它部署的初始状态?

与 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
});
  1. 我习惯于在多个组件中使用 Redux 减速器,但是 useReducer 需要一个传递的 initialState ......这就提出了一个问题:react-hook 的减速器是否可以通过多个组件作为单个实例使用,还是始终是 2 个单独的实例?

以下useReducer是与 Redux 的不同之处:您可以重用 reducer 本身,但如果您有多个useReducers,则每个返回的statedispatch以及初始状态将是单独的实例。

为了在 reducer 更新时测试 BarComponent 是否更新,您需要一种dispatch从组件内部触发的方法,因为您是useReducer在组件内部调用的。这是一个例子:

export function BarComponent() {
  const [state, dispatch] = useReducer(FooReducer, []);

  const handleUpdate = () => dispatch({type: 'update', newState: [1, 2, 3]})

  return (
    <>
      {state.map((item) => (<div />))}
      <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);
});

这可能不太现实,因为我在组件本身中对新数组进行了硬编码,但希望它能给您带来想法!

于 2019-12-11T01:38:38.610 回答