1

My project is to make typing animation effect and on the end of the word which is typed I need to slow down the speed of the blinker.

I'm trying to set Interval speed with a setState functionality. The only problem is that I cannot setState from within render method but exactly there I have an access to my counter. And I guess I need to use my counter state in order to know when I need to slow the blinker down. I hope my description makes some sense :-)

Thank you for any help :)

Here's my code:

import React, { Component } from 'react';

class TypeAnimation extends Component {
    constructor(props) {
        super(props);
        this.state = {
            sec: 0,
            blinker: '',
            blinkerSpeed: 100
        };
    }

    componentDidMount() {
        this.textInterval = setInterval(() => {
            this.setState({
                sec: this.state.sec + 1
            });
        }, 200);

        this.blinkerInterval = setInterval(() => {

            if (this.state.blinker === '') {
                this.setState({
                    blinker: <span style={{ color: 'orange', lineHeight: '2rem' }}> | </span>
                });
            } else {
                this.setState({
                    blinker: ''
                });
            }
        }, this.state.blinkerSpeed); //here I would like to change the speed of the blinker with a state
    }
    render() {
        const inText = this.props.text[0];
        if (this.state.sec === this.props.text[0].length) {
            clearInterval(this.textInterval);
        }

        const firstLine = inText.substr(0, this.state.sec);

        return (
            <div style={{ diplay: 'flex', justifyContent: 'center', marginTop: 30 }}>
                <h1>
                    {firstLine}
                    {this.state.blinker}
                </h1>
            </div>
        );
    }
}

export default TypeAnimation;
Murakami
  • 3,474
  • 7
  • 35
  • 89

1 Answers1

1

This is a classical example of using componentDidUpdate.

Your clearInterval should not be happening in the render, but in componentDidUpdate. You can then use a setState in that lifecycle.

componentDidUpdate(prevProps, prevState) {
  const newSpeed = 500; // change this number
  if (this.state.sec === this.props.text[0].length && this.state.blinkerSpeed !== newSpeed) {
        clearInterval(this.textInterval);
        this.setState({ blinkerSpeed: newSpeed })
    }
}

It's crucial that you have another qualifier in your if statement that makes sure the blinker speed hasn't already been changed or else you'll get an infinite render loop.

EDIT:

In the end, I wouldn't use React at all to do this. I would use CSS. By using react, you are re rendering the state every __ milliseconds which is terrible for performance


more info: https://developmentarc.gitbooks.io/react-indepth/content/life_cycle/update/postrender_with_componentdidupdate.html

Craig1123
  • 1,510
  • 2
  • 17
  • 25
  • Thank you for your answet but unfortunately the chunk of code doesn't work. I'm getting this error: "Cannot read property 'blinkerSpeed' of undefined", also EsLint says that "Do not use setState in componentDidUpdate", but this may be not so important as the first issue. – Murakami Feb 09 '18 at 20:38
  • My answer wasn't supposed to be a copy-pasta answer but a reference how to write your own. I am definitely not getting that error, check your copy-paste again. And you need to understand the reasoning behind the eslint rule. The rule is created to prevent developers from creating an infinite render loop. I clearly mentioned that in my answer. Good luck :) – Craig1123 Feb 09 '18 at 20:50
  • Sure! :) Thank you! sorry for bothering you but when I'm setting blinkerSpeed value with setState to 500 then I'm getting: Maximum call stack size exceeded and console is going crazy with lots of error logs – Murakami Feb 09 '18 at 20:56
  • Yeah, you're getting the maximum call stack size exceeded because it's an infinite loop. If you changed the blinker speed to 500, you also need to change the checker in the if statement to 500. I'll update my code to reflect this – Craig1123 Feb 09 '18 at 21:01
  • Thank you for that! When I log this it is changing state and it is big step forward.For some reason though it does not affect Interval itself and the speed stays the same. May it be because componentDidMount and Interval within it renders only once and that is the reason it won't get the new value of 500? – Murakami Feb 09 '18 at 21:12
  • Exactly, you have to change the other interval too. In the end, this is not very performant to cause so many re-renders. I would use CSS. Here is a link to get you on that path https://stackoverflow.com/questions/16344354/how-to-make-blinking-flashing-text-with-css-3 – Craig1123 Feb 09 '18 at 21:16
  • Thank you so much for all your help, will try to solve this asap :) – Murakami Feb 09 '18 at 21:18