dannymcgee
dannymcgee

Reputation: 673

React: Updating state in a conditional setTimeout inside componentDidMount does not reload the component

EDIT: I figured out the problem shortly after posting this. See my answer below.

I'm really stumped as to why my component won't reload after updating the state. This is the code for the component in full (minus the imports/export), with annotations:

class About extends Component {
    state = {
        introIsRunning: true,
        animationStep: 1,
        indent: 0,
        steps: {
            1: ["placeholder"],
            2: ["placeholder"],
            3: ["placeholder"],
            4: ["placeholder"],
            5: ["placeholder"],
            6: ["placeholder"],
        },
        content: [],
    };

My goal is to show a new line of content every five seconds until I reach the end of the steps, at which point I stop.

Before I make any state changes, I check to make sure if introIsRunning is true. Then I check to see if we've reached the end of the steps; if so, I set introIsRunning to false which will prevent anymore state changes.

(Looking at this again, I see it's pretty redundant to have the boolean and the animationStep check when I can just check for the animationStep and leave it at that. I can't see why that would be the problem though, so moving on...)

    componentDidMount = () => {
        if (this.state.introIsRunning) {
            if (this.state.animationStep > 6 {
                this.setState({ introIsRunning: false });
            }

If this is the first step, I don't want any delay before displaying the first line. But if at least the first line has already been returned, I want to wait 5 seconds before adding the next. However, because zero delay would trigger an immediate state change which could potentially cause issues, I've added a 5 second delay to the first line rendering just to make sure that wasn't causing my issue.

            let animationDelay = 5000;
            if (this.state.animationStep > 1) {
                animationDelay = 10000;
            }

            console.log('state before timeout:', this.state);

The 'state before timeout' console log shows animationStep: 1, and content is an empty array. As expected.

After the prescribed delay, I update the state with the correct step's content and increment the step so that on the next iteration, it'll load step 2 and advance to 3, etc.

setupContent just adds a new object with the appropriate content to the state's content array, which gets used in the render method to build out the actual component.

            setTimeout(() => {
                this.setState({
                    content: this.setupContent(this.state.steps[this.state.animationStep]),
                    animationStep: this.state.animationStep + 1,
                });
            }, animationDelay);

            // debugging
            setTimeout(() => {
                console.log('why am I still here? state is:', this.state);
            }, 15000);
        }
    }

    setupContent = content => {
        let updatedContent = [...this.state.content];

        const id = "line_" + this.state.animationStep;

        updatedContent.push({
            id: id,
            number: this.state.animationStep,
            indent: this.state.indent,
            content: content,
        });
    }

The 'why am I still here?' console log actually shows the correctly updated state. animationStep is now 2, and the content array contains one object built to setupContent's specifications.

    render() {
        return (
            <div className="container">
                {this.state.content.map(line => (
                    <Line
                        key={line.id}
                        id={line.id}
                        number={line.number}
                        indent={line.indent}
                    >
                        {line.content}
                    </Line>
                ))}
            </div>
        );
    }
}

This correctly turns my content object into a functional Component which is rendering how I want it to. However, it just never advances to the next step. After correctly, verifiably updating the state, it just sits here. I've searched around and done as much debugging as I can think to do but I really have no earthly idea why the state would update without reloading the component.

Upvotes: 0

Views: 717

Answers (1)

dannymcgee
dannymcgee

Reputation: 673

Wow, I feel really dumb for figuring this out minutes after posting the question, but the problem — of course — was that I was making all those checks in componentDidMount, which only happens once. I moved all the code out of componentDidMount and into its own function, which I now call in componentDidMount and componentDidUpdate. Now it's working as expected. I should really get a rubber duck...

Upvotes: 1

Related Questions