为什么setState interrupt componentDidUpdate?

时间:2018-03-01 17:56:35

标签: reactjs unit-testing enzyme jest

我有这个组件(简化版):

export default class MyComponent extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            isLoading: false,
            data: {}
        };
    }

    componentDidUpdate(prevProps, prevState) {
        if(this.props.time && this.props.time !== prevProps.time){
           this.setState({
             isLoading: true
           })
           fetch(...).then(data => {
             this.setState({
               data: data
               isLoading:false
             }
        }
    }
    render(){
      {isLoading, data} = this.state;
      return (isLoading ? /*show spinner*/ : /* show data*/);
    }

}

此组件有效:它在获取数据时显示微调器,然后显示数据。

我正在尝试使用jest和酶来测试它:

test('Mounted correctly', async() => {
   let myComponent = mount(<MyComponent time='01-01-18'/>);
   myComponent.setProps({time: '02-01-18'}); //necessary to call componentDidUpdate
   expect(myComponent.state()).toMatchSnapshot();
}

据我所知,为了致电componentDidUpdate ,您必须致电setProslink)。但是,在调试器之后,当命中时调用结束:

       this.setState({
         isLoading: true
       })

这是预期的,问题是快照是:

Object {
  "isLoading": true
  "data": {}
}

当然,这是我不想要的东西。我该如何解决这个问题?

更新:我找到了一个(难看的)解决方案!

问题在于我们要测试的是setState已完成:

         this.setState({
           data: data
           isLoading:false
         }

现在,即使设置await myComponent.setProps({time: '02-01-18'});(如其中一个答案中所建议的),也不会发生这种情况,因为它不会等待上述setState创建的新异步调用。

我找到的唯一解决方案是将回调函数传递给props并在setState完成后调用它。回调函数包含我们想要的expect

所以这是最终结果:

test('Mounted correctly', async() => {
   let myComponent = mount(<MyComponent time='01-01-18'/>);
   const callBackAfterLastSetStateIsCompleted = () => {
      expect(topAsins.state()).toMatchSnapshot();
   }
   myComponent.setProps({time: '02-01-18', testCallBack: callBackAfterLastSetStateIsCompleted}); //necessary to call componentDidUpdate
   expect(myComponent.state()).toMatchSnapshot();
}

将组件代码修改为:

         this.setState({
           data: data
           isLoading:false
         },this.props.testCallBack);

但是,正如您所看到的,我正在修改生产中的组件仅用于测试目的,这是非常丑陋。

现在,我的问题是:我该如何解决这个问题?

1 个答案:

答案 0 :(得分:1)

您需要在此处进行测试,只需使用async/await

test('Mounted correctly', async () => {
   let myComponent = mount(<MyComponent time='01-01-18'/>);
   await myComponent.setProps({time: '02-01-18'}); //necessary to call componentDidUpdate, await used to wait for async action in componentDidUpdate
   expect(myComponent.state()).toMatchSnapshot();
}