Test中的ReactDOM.unmountComponentAtNode()会导致警告

时间:2018-02-05 13:06:50

标签: reactjs

我构建了我的应用程序wir create-react-app。在最近的版本中,它在Jest中的引导测试设置中添加了一行来卸载组件(参见ReactDOM.unmountComponentAtNode(div))。

import ReactDOM from 'react-dom';
import App from './App';

it('renders without crashing', () => {
  const div = document.createElement('div');
  ReactDOM.render(<App />, div);
  ReactDOM.unmountComponentAtNode(div);
});

当我为我的App组件运行测试时会发出警告。

警告:只能更新已安装或安装的组件。这通常意味着您在已卸载的组件上调用了setState,replaceState或forceUpdate。这是一个无操作。

我猜:发生这种情况是因为我在componentDidMount()中有异步请求:

fetchFoo(bar) {
  fetch(SOME_URL)
    .then(response => response.json())
    .then(result => this.setState({ result }))
    .catch(error => this.setState({ error }));
}

如果是这种情况,我如何在测试中等待异步请求最终再次卸载组件?我知道我可以在Jest测试中移除一个衬里导致这个,但我想修复它。

解决方案:解决了问题并记录了如何通过here解决问题。

2 个答案:

答案 0 :(得分:9)

解决此问题的最简单方法是在卸载组件时使fetch()正确取消。在componentDidMount() 中具有不自行取消的异步请求是React的不良做法,因为根据网络速度和UI交互,他们可以经常尝试更新未安装组件的状态。要么使用可取消的Promise,要么使用this.shouldCancel实例变量来指示要调用setState()的天气,如下所示:

class LifecycleTest extends React.Component {
  constructor(props){
    super(props)
    this.shouldCancel = false;
    this.state = {
      result:null,
      err:null
    }
  }

  componentDidMount(){
    asyncTask()
      .then(result => !this.shouldCancel ? this.setState({result}) : null)
      .catch(err => !this.shouldCancel ? this.setState({err}) : null);
  }

  componentWillUnmount(){
    this.shouldCancel = true
  }

  render(){
    const {err, result} = this.state
    if(err){
      return <div className="err">{err}</div>
    }else if (result){
      return <div className="result">{result}</div>
    }else{
      return <div className="loading">Loading...</div>
    }        
  }
}

view on webpackbin

也就是说,如果您真的希望在不更改源代码的情况下进行此测试,则可以模拟fetch()以返回永不解析的'dud'Promise。例如,添加它应该可以修复错误:

window.fetch = jest.fn(() => new Promise((accept, reject) => {}))

然而,这是一个可怕的黑客。更好的解决方案是在组件卸载时正确取消网络请求。

答案 1 :(得分:2)

您可以在React docs中看到componentDidMount()是处理网络请求的推荐位置。

您收到此错误的原因是您在异步调用中使用setState并且不受React生命周期的限制,因此您最终会在组件卸载后尝试设置好状态。

您处理/更正此问题的机会将在componentWillUnmount()。这需要在fetch()方法期间以某种方式取消您的componentWillUnmount()请求。您需要在可取消的承诺中包装您的网络请求才能执行此操作,因为fetch()本身不支持直接取消请求。

最终,这会阻止在卸载组件后setState()被称为