Reputation: 145
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
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
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
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