Reputation: 438
I have a simple web app with one parent component and three child components. The children are just div boxes that can change their background color from white to red based on their property active. The animateChild() function can be used to turn a specific child red and then white again after 1s.
My goal is to "play" an arbitrary sequence of child animations with a break of 1s between each animation. This should happen in the animateSequence() function.
My initial idea to accomplish that, was to use a for loop and call animateChild(childNum) in a window.setTimeout() at each iteration. However, this didn't work. I guess because setState() is asynchronous and several state changes get merged together.
What is the best way to accomplish a sequence (e.g. Child 1, Child 2, Child 3) of animations with a break time of 1s between each animation? Thank you very much
Javascript Code:
class Child extends React.Component {
render() {
var childStyle = "basic";
if (this.props.active){
childStyle += " active";
}
return (
<div className={childStyle}></div>
);
}
}
class Parent extends React.Component {
constructor(){
super();
this.state = {
firstChildActive:false,
secondChildActive:false,
thirdChildActive:false,
};
this.animateChild = this.animateChild.bind(this);
}
animateChild(childNum){
var childDict = ["firstChildActive","secondChildActive","thirdChildActive"];
this.setState({[childDict[childNum]]: true}, function() {
window.setTimeout(() => {
this.setState({[childDict[childNum]]: false});
},1000);
});
}
animateSequence(){
// animate child 2, then child 1, then child 3, then child 1, etc.
}
render() {
return (
<div className ="container">
<Child active={this.state.firstChildActive}/>
<Child active={this.state.secondChildActive}/>
<Child active={this.state.thirdChildActive}/>
</div>
);
}
}
ReactDOM.render(<Parent />,document.getElementById('app'));
CSS Code:
.container{
display:flex;
width:100%;
justify-content: space-between;
}
.basic{
margin: 10px;
width: 100px;
height:100px;
border: 2px solid black;
}
.active{
background: red;
}
Codepen: https://codepen.io/miga89/full/owgZea/
Upvotes: 0
Views: 401
Reputation: 258
if you want do that without button use componentDidMount method in prototype:
componentDidMount(){
this.animateSequence();
}
render() {
return (
<div className ="container">
<Child isActive={this.state.firstChildActive }/>
<Child isActive={this.state.secondChildActive } />
<Child isActive={this.state.thirdChildActive }/>
</div>
);
}
Upvotes: 0
Reputation: 5584
I've also converted your Child Component into a Stateless Component, the solution was just use a simple setInterval.
https://codepen.io/anon/pen/LLEyVo
const Child = ({ isActive }) => {
var childStyle = "basic";
if ( isActive ) childStyle += " active";
return <div className={ childStyle }></div>
}
class Parent extends React.Component {
constructor(props){
super(props);
this.state = {
firstChildActive : false,
secondChildActive : false,
thirdChildActive : false,
};
}
animateSequence(){
const TOTAL = 3;
const CHILD_DICT = ["firstChildActive","secondChildActive","thirdChildActive"];
let run = 0;
var setActive = setInterval( () => {
this.setState({ [CHILD_DICT[run]]: true });
run = run + 1;
if( run === TOTAL ) {
clearInterval(setActive);
}
}, 1000)
}
render() {
return (
<div className ="container">
<button onClick={ () => this.animateSequence() }>Run Sequence</button>
<Child isActive={this.state.firstChildActive }/>
<Child isActive={this.state.secondChildActive } />
<Child isActive={this.state.thirdChildActive }/>
</div>
);
}
}
ReactDOM.render(<Parent />,document.getElementById('app'));
Upvotes: 1
Reputation: 1744
Your this context is invalid in the callback of first setState.
var childDict = ["firstChildActive","secondChildActive","thirdChildActive"];
this.setState({[childDict[childNum]]: true}, function() {
Should be
var childDict = ["firstChildActive","secondChildActive","thirdChildActive"];
this.setState({[childDict[childNum]]: true}, () => {
Upvotes: 0