0

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

3
  • I'm voting to close this question as off-topic because the code is working as is. The question belongs on codereview.stackexchange.com Commented Jan 18, 2019 at 9:33
  • 1
    This is a lot less to do with setTimeout than simply refactoring, I would wrap each callback into a promise and call them via await Commented Jan 18, 2019 at 9:35
  • @YuryTarabanko I am sorry to see your close vote. Unfortunately there no even views in codereview page codereview.stackexchange.com/questions/211710/… Commented Jan 18, 2019 at 9:35

2 Answers 2

1

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:

  1. You want to start with typingEffect
  2. Once 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

Sign up to request clarification or add additional context in comments.

3 Comments

Thank you very much for your help buddy. FYI codesandbox.io/s/308kxjzwrq still same as the original code
@Mo. My bad, looks like I forgot to hit save :) fixed now!
got it, looks awesome 👌
0

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.