等待 React 组件完成更新
Wait for a React Component to finish updating
在我的测试中,我想阻塞我的主线程,直到我的一个组件通过 componentDidUpdate()
完成其生命周期方法,在我触发一个导致它向自己添加子组件的事件之后.我该怎么做?
像这样:
describe('my <Component />', () => {
it('should do what I want when its button is clicked twice', () => {
const wrapper = mount(<Component />);
const button = wrapper.find('button');
// This next line has some side effects that cause <Component /> to add
// some children components to itself...
button.simulate('click', mockEvent);
// ... so I want to wait for those children to completely go through
// their lifecycle methods ...
wrapper.instance().askReactToBlockUntilTheComponentIsFinishedUpdating();
// ... so that I can be sure React is in the state I want it to be in
// when I further manipulate the <Component />
button.simulate('click', mockEvent);
expect(whatIWant()).to.be.true;
});
});
(我想这样做是因为,现在,我收到了这个警告:
Warning: setState(...): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op.
我相信我明白了,因为我的测试导致我的组件改变其内部状态的速度比 React 的内部多线程魔法跟上的速度更快,所以到我 运行 button.simulate('click')
第二次,React 已经实例化了新的子组件,但还没有完成挂载。我认为等待 React 完成更新我的组件及其子组件是解决该问题的最佳方法。)
尝试将 expect()
块包裹在 setImmediate()
块中:
describe('my <Component />', () => {
it('should do what I want when its button is clicked twice', (done) => {
const wrapper = mount(<Component />);
const button = wrapper.find('button');
button.simulate('click', mockEvent);
button.simulate('click', mockEvent);
setImmediate(() => {
expect(whatIWant()).to.be.true;
done();
});
});
});
事情是这样的:为了处理异步性,Node 和大多数浏览器在幕后都有一个事件队列。每当某些东西(例如 Promise 或 IO 事件)需要异步 运行 时,JS 环境会将其添加到队列的末尾。然后,每当一段同步代码完成 运行ning 时,环境就会检查队列,选择队列前面的任何内容,然后 运行 就这样。
setImmediate()
在队列后面添加一个函数。一旦当前队列中的所有内容完成 运行ning,传递给 setImmediate()
的函数中的任何内容都将 运行。因此,无论 React 正在异步执行什么操作,将您的 expect()
s 包装在 setImmediate()
中都会导致您的测试等待 React 完成它在幕后执行的任何异步工作。
这是一个很好的问题,其中包含有关 setImmediate()
的更多信息:setImmediate vs. nextTick
这是 Node 中 setImmediate()
的文档:https://nodejs.org/api/timers.html#timers_setimmediate_callback_args
在我的测试中,我想阻塞我的主线程,直到我的一个组件通过 componentDidUpdate()
完成其生命周期方法,在我触发一个导致它向自己添加子组件的事件之后.我该怎么做?
像这样:
describe('my <Component />', () => {
it('should do what I want when its button is clicked twice', () => {
const wrapper = mount(<Component />);
const button = wrapper.find('button');
// This next line has some side effects that cause <Component /> to add
// some children components to itself...
button.simulate('click', mockEvent);
// ... so I want to wait for those children to completely go through
// their lifecycle methods ...
wrapper.instance().askReactToBlockUntilTheComponentIsFinishedUpdating();
// ... so that I can be sure React is in the state I want it to be in
// when I further manipulate the <Component />
button.simulate('click', mockEvent);
expect(whatIWant()).to.be.true;
});
});
(我想这样做是因为,现在,我收到了这个警告:
Warning: setState(...): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op.
我相信我明白了,因为我的测试导致我的组件改变其内部状态的速度比 React 的内部多线程魔法跟上的速度更快,所以到我 运行 button.simulate('click')
第二次,React 已经实例化了新的子组件,但还没有完成挂载。我认为等待 React 完成更新我的组件及其子组件是解决该问题的最佳方法。)
尝试将 expect()
块包裹在 setImmediate()
块中:
describe('my <Component />', () => {
it('should do what I want when its button is clicked twice', (done) => {
const wrapper = mount(<Component />);
const button = wrapper.find('button');
button.simulate('click', mockEvent);
button.simulate('click', mockEvent);
setImmediate(() => {
expect(whatIWant()).to.be.true;
done();
});
});
});
事情是这样的:为了处理异步性,Node 和大多数浏览器在幕后都有一个事件队列。每当某些东西(例如 Promise 或 IO 事件)需要异步 运行 时,JS 环境会将其添加到队列的末尾。然后,每当一段同步代码完成 运行ning 时,环境就会检查队列,选择队列前面的任何内容,然后 运行 就这样。
setImmediate()
在队列后面添加一个函数。一旦当前队列中的所有内容完成 运行ning,传递给 setImmediate()
的函数中的任何内容都将 运行。因此,无论 React 正在异步执行什么操作,将您的 expect()
s 包装在 setImmediate()
中都会导致您的测试等待 React 完成它在幕后执行的任何异步工作。
这是一个很好的问题,其中包含有关 setImmediate()
的更多信息:setImmediate vs. nextTick
这是 Node 中 setImmediate()
的文档:https://nodejs.org/api/timers.html#timers_setimmediate_callback_args