Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wait for a React Component to finish updating

Tags:

In my tests, I would like to block my main thread until one of my components finishes going through its lifecycle methods, through componentDidUpdate(), after I trigger an event that causes it to add children components to itself. How can I do so?

Something like this:

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;
  });
});

(I want to do this because, right now, I get this warning:

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.

I believe I'm getting it because my tests cause my component to change its internal state more quickly than React's internal multithreading magic can keep up with, so by the time I i.e. run button.simulate('click') the second time, React has instantiated the new child components but hasn't finished mounting them yet. I think that waiting for React to finish updating my Component and its children is the best way to solve that problem.)

like image 226
Kevin Avatar asked Dec 22 '16 17:12

Kevin


People also ask

How do you set a delay in React?

Define a function that takes the number of milliseconds as parameter. Use the setTimeout method to resolve a Promise after the provided number of milliseconds.

How do you wait for a setState to finish?

To wait for setState to finish before triggering a function with React. js, we call setState with a callback that runs after the state is updated as the 2nd argument. this. setState( { originId: input.

Why isn't my React component updating?

There are two common reasons why React might not update a component even though its props have changed: The props weren't updated correctly via setState. The reference to the prop stayed the same.

How do I force an update in React?

The forceUpdate() method With this method we will achieve the long-awaited forced rendering of a React component. Calling forceUpdate() will cause the component to be rendered to be called and shouldComponentUpdate() will be skipped.


1 Answers

Try wrapping your expect() blocks in a setImmediate() block:

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();
    });
  });
});

Here's what's going on: To handle asynchronicity, Node and most browsers have an event queue behind the scenes. Whenever something, like a Promise or an IO event, needs to run asynchronously, the JS environment adds it to the end of the queue. Then, whenever a synchronous piece of code finishes running, the environment checks the queue, picks whatever is at the front of the queue, and runs that.

setImmediate() adds a function to the back of the queue. Once everything that is currently in the queue finishes running, whatever is in the function passed to setImmediate() will run. So, whatever React is doing asynchronously, wrapping your expect()s inside of a setImmediate() will cause your test to wait until React is finished with whatever asynchronous work it does behind the scenes.

Here's a great question with more information about setImmediate(): setImmediate vs. nextTick

Here's the documentation for setImmediate() in Node: https://nodejs.org/api/timers.html#timers_setimmediate_callback_args

like image 175
Kevin Avatar answered Sep 22 '22 16:09

Kevin