kwong
kwong

Reputation: 145

Updating parent component only after multiple child components have completed running

I have a Parent react component with multiple child components that are created through a .map() function. I am passing in a function addCallback() as child props so I have a reference and can trigger all child's handleRun() function via the Parent.

I'm trying to update state of my Parent component to running = true when all children are running and to running = false and render said status on the parent when all children have completed running. However the state doesn't seem to update in the particular sequence I specify.

Here is how I'm doing it:

let promise1 = this.setState({isRunning:  true},
            () => {
                this.state.childRef.map(x => x())
            });

Promise.all([promise1])
    .then(() => this.setState({isRunning: false}))

Here's the entire code in codesandbox: link

Would appreciate your help as I'm still pretty new to React (and Javascript in general). Thanks!

Upvotes: 3

Views: 215

Answers (3)

maazadeeb
maazadeeb

Reputation: 6112

Using async in a function declaration automatically returns a Promise wrapped around whatever you are returning from your function. In your case, it's undefined. This is why your current code is not throwing any errors at the moment.

You will need a mechanism to wait for the setTimeout. Changing the runSomething function like this will work

  async runSomething() {
    this.setState({ status: "running" });

    // simulate running something that takes 8s
    return new Promise(resolve => {
      setTimeout(() => {
        this.setState({ status: "idle" }, resolve);
      }, 3000);
    });
  }

Do notice the line this.setState({ status: "idle" }, resolve);. It makes sure that your promise resolves not only after the setTimeout but also after the child's state is changed to "idle". Which is the correct indication that your child component has moved to "idle" state.

Codesandbox: https://codesandbox.io/s/epic-boyd-12hkj

Upvotes: 2

Sagar Khan
Sagar Khan

Reputation: 302

Here is the sandbox implementation of what you are trying to achieve. Sanbox

Here i have created a state in parent component that will be updated when child is running.

this.state = {
  callbacks: [],
  components: [
    {
      index: 0, // we don't need this field its just for your info you can just create [true,false] array and index will represent component index.
      status: false
    },
    {
      index: 1,
      status: false
    }
  ]
};

When all the status in component array is true we update the idle status of parent to running.

  getAllRunningStatus() {
    let { components } = this.state;
    let checkAllRunning = components.map(element => element.status);
    if (checkAllRunning.indexOf(false) === -1) { // you can also use !includes(false) 
      return true;
    }
    return false;
  }

inside your render function

<h1>Parent {this.getAllRunningStatus() ? "running" : "idle"}</h1>

Note:- I have just written a rough code. You can optimise it as per your requirements. Thanks

Upvotes: 0

Giang Le
Giang Le

Reputation: 7054

Cause runSomething is not a Promise. You must change.

runSomething() {
  return new Promise((resolve, reject) => {
    this.setState({ status: "running" });
     // simulate running something that takes 8s
     setTimeout(() => {
       this.setState({ status: "idle" });
       resolve(true);
     }, 3000);
  });
}

A working sandbox here https://codesandbox.io/s/fragrant-cloud-5o2um

Upvotes: 2

Related Questions