reactjs - 酶不反映异步 setState 回调中的 setState 更改
问题描述
我试图弄清楚这一天,但我无法让它发挥作用。
我有一个更新内部状态的组件componentDidMount
,然后在setState's
回调中我再次更新状态,但回调异步运行。这是因为我在那里发出了一个 http 请求,我用它的响应来更新状态。在单元测试期间,我模拟了这个模块,所以它不会到达网络。但是 Enzyme 并不反映异步回调中的状态变化。这是有道理的,因为它是异步的,但我还没有找到任何解决方法。
我已经构建了我想要实现的缩小版本。
这是我的组件:
class Component extends React.Component {
constructor(props) {
super(props);
this.state = {
value: 0,
};
this._isMounted = false;
}
componentDidMount() {
this._isMounted = true;
this.setState(state => {
const { value } = this.state;
return {
...state,
value: value + 15,
};
}, async () => {
try {
// If you remove this line, it works.
await Promise.resolve(15);
if (this._isMounted) {
this.setState(state => {
const { value } = this.state;
// If you console.log during testing,
// this value is updated correctly, but Enzyme doesn't reflect it.
return {
...state,
value: value + 15,
};
});
}
} catch(e) {
// Do nothing...
}
});
}
componentWillUnmount() {
this._isMounted = false;
}
render() {
const { value } = this.state;
return (
<div>{ value } </div>
);
}
}
这是我的测试:
describe('Component Enzyme Bug Testing', () => {
it('should update value properly', () => {
const wrapper = shallow(<Component />, { disableLifecycleMethods: true });
wrapper.instance().componentDidMount();
expect(wrapper.state().value).toEqual(30);
});
});
在测试运行中,state().value
是 15,而预期是 30。这意味着它注册了第一个更改,但没有注册第二个更改。
解决方案
当在 Node 中执行 IO 绑定代码时,这些操作的回调不会在当前上下文中运行,它们被放回事件循环(更多信息),然后在下一个“tick”时执行。这实际上意味着,当您运行调用异步代码的同步代码时,即使异步代码实际上不是异步的(例如Promise.resolve
),它仍然会在同步代码运行完成后运行。
在您的情况下,正如您在代码注释中提到的那样,测试仅在您引入时才开始失败Promise.resolve
,这是因为此时我们将状态更改转移到不同的上下文,然后测试运行完成并且断言失败(因为您希望现在能理解为什么)。
要修复测试,它相当简单,知道状态更新现在将在下一个滴答时运行,我们只是想安排我们的断言来做同样的事情:
it('should update value properly', done => {
const wrapper = shallow(<Component />, { disableLifecycleMethods: true });
wrapper.instance().componentDidMount();
process.nextTick(() => {
expect(wrapper.state().value).toEqual(30);
done();
})
});
这使断言与状态更新在同一滴答上运行,并且根据文章,我们知道事件循环阶段遵循 FIFO 模型,因此我们可以保证断言将在状态更新后运行。
推荐阅读
- php - 如何获取 $_GET URL(有时它会创建一个包含多个元素的数组)
- ionic3 - Ionic 4 未在 HTML 中显示数据
- php - 为每一天创建一个包含多个条目的每周数组
- c# - 我尝试在 unity-webGL 中构建我的游戏,但出现错误
- python - 如何用另一个ndarray索引一个ndarray?
- javascript - 如何使用用户定义的过滤器或其他方法控制通过 CSS 模块或样式组件自动生成的动态类名
- c++ - Qt QTabWidget 背景颜色
- python - 如何用 flask_mongoengine 关闭数据库?
- r - 如何在使用 UTF-8 字符清理数据的 r 中写入 csv?
- python-3.x - np.genfromtxt 和 StringIO 中的 dtype 参数