Reputation: 27445
There was a typewriting animation in pure javaScript which has been converted to ReactJS. The setTimeout
functions do not look clean and do not adhere to best practices according to ReactJS standard.
For example animationManager()
animationManager = () => {
this.rafRef = requestAnimationFrame(time => {
const typingData = this.props.data;
this.typeEffect(time, typingData[this.index], () => {
this.timeoutRef = setTimeout(() => {
this.rafRef = requestAnimationFrame(time => {
this.deleteEffect(time, () => {
this.timeoutRef = setTimeout(() => {
this.index =
this.index === typingData.length - 1 ? 0 : this.index + 1;
this.animationManager();
}, this.props.pauseBeforeRestarting);
});
});
}, this.props.pauseBeforeDeleting);
});
});
};
Is it possible to make it more clean with all these setTimout
?
Complete code 👉 https://codesandbox.io/s/qk4591q1kw
Upvotes: 0
Views: 447
Reputation: 66103
Yes, you can actually create functions that acts like a timer: it returns a promise that is resolved when the time runs out, something like this:
timer = (duration) => {
return new Promise(resolve => {
window.setTimeout(resolve, duration);
});
}
Similarly, you can do the same for requestAnimationFrame
. The trick is to use ES6 spread operator so that you can pass arbitrary number of arguments into the callback to be invoked:
animationFrame = (callback, ...args) => {
return new Promise(resolve => {
window.requestAnimationFrame(time => {
callback(time, ...args);
});
})
}
Since you are using ES6, you can then use async
functions to wait for the timer to complete, before moving on to execute the next line of code. If we break down your animationManager()
code, it can be seen as following:
typingEffect
typingEffect
is completed, you want to trigger deleteEffect
In this case, we can refactor your code as such:
animationManager = () => {
const deleteFunc = (time, typingData) => {
this.deleteEffect(time, async () => {
await this.timer(this.props.pauseBeforeRestarting);
this.index = this.index === typingData.length - 1 ? 0 : this.index + 1;
this.animationManager();
});
};
const typeFunc = (time) => {
const typingData = this.props.data;
this.typeEffect(time, typingData[this.index], async () => {
await this.timer(this.props.pauseBeforeDeleting);
await this.animationFrame(deleteFunc, typingData);
})
};
this.animationFrame(typeFunc);
};
I have forked your example to provide a proof-of-concept of the slightly refactored code: https://codesandbox.io/s/308kxjzwrq
Upvotes: 1
Reputation: 1542
The common practice is to use Promises for that. You can create helper Promise which will use requestAnimationFrame, and make your flow flat and "thenable", by adding success callbacks onResolve.
Upvotes: 0