Search code examples
javascriptreactjs

Typewriter effect in React


I'm trying to make a typewriting effect in a React component. I pass in an array of strings and try to render them character by character after some interval (using setTimeout). Here is my code for that

  state = {
    typeColor: {
      color: "blue"
    },
    typed: ""
  };

  componentDidMount() {
    this.props.typewriter.map(string => {
      console.log("1", string);
      return this.stringChecker(string);
    });
  }

  typeWriter(string) {
    if (string.length === 0) {
      return;
    }
    console.log("3", string);
    this.setState((state, props) => ({ typed: state.typed.concat(string[0]) }));
    console.log(this.state.typed);

    setTimeout(() => this.typeWriter(string.slice(1)), 120);
  }
  stringChecker(string) {
    console.log("2", string);
    if (string === "Qurram") {
      this.setState({ typeColor: { color: "blue" } });
    } else {
      this.setState({ typeColor: { color: "pink" } });
    }
    this.typeWriter(string);
  }

  render() {
    return <div style={this.state.typeColor}>{this.state.typed}</div>;
  }
}

Now I was expecting the execution flow of this to go like this: first element of string array is 'selected' in the map function -> i call stringchecker for that first element -> i call typewriter for that first element, and iterate over the characters. then i go back to the map function and do the same for next. It seems the flow is not like this at all,instead it alternates between the two strings for each character slice. I'd really appreciate if someone could explain that to me. Thank you very much

Sandbox link : https://codesandbox.io/s/morning-dust-18nq5


Solution

  • The typing logic is good, however, note that setTimeout is async, so it doesn't wait for the whole word to be typed before switching to the next one. The solution is to make the whole typing flow async, so you don't switch to the next word before the previous one has been typed.

    Here's the sandbox demo: https://codesandbox.io/s/boring-greider-824nd

    The main change is this one:

    if (string.length === 0) {
      words = words.slice(1);
      words[0] && this.typeWord(words[0], words);
    }
    

    Check if we finished typing a word, then slice it, to get the next word. If we still have words we call typeWord with the word that should be typed and the array of words that should be typed next. If there are no words left, we exit typing.