3

I have an array stored as a prop:

constructor(props) {
    super(props);

    this.letters = [];
}

Inside a componentDidMount method, I put this prop in a forEach loop that has a setTimeout in it:

componentDidMount() {
    this.letters.forEach((letter, index) => {
        setTimeout(function() {
            letter.classList.add('stroke');
        }, index * 125);
    });
}

This works as expected, adding a class to each element inside the prop array.

Then I tried adding a second forEach loop after this one. This looks a little confusing, but it's basically the same as the first forEach loop, but it's wrapped inside a setTimeout so that it starts 1 second later:

    this.letters.forEach((letter, index) => {
        setTimeout(function() {
            letter.classList.add('stroke');
        }, index * 125);
    });

    setTimeout(function() {
        this.letters.forEach((letter, index) => {
            setTimeout(function() {
                letter.classList.add('fill');
            }, index * 125);
        });
    }, 1000);

With these two forEach loops on this.letters back to back, I get this error on the this.letters.forEach... line of the second loop:

TypeError: Cannot read property 'forEach' of undefined

I tried setting this.letters as another variable inside componentDidMount, and used that variable instead of this.letters:

    const letters = this.letters;

    letters.forEach((letter, index) => {
        setTimeout(function() {
            letter.classList.add('stroke');
        }, index * 125);
    });

    setTimeout(function() {
        letters.forEach((letter, index) => {
            setTimeout(function() {
                letter.classList.add('fill');
            }, index * 125);
        });
    }, 1000);

This works, but I'm not sure I understand why this.letters doesn't work?

I also tried replacing just one of the loops with the letters variable instead of this.letters -- the first loop works fine with this.letters, but the second loop doesn't work with this.letters no matter what.

rpivovar
  • 3,150
  • 13
  • 41
  • 79
  • 2
    `setTimeout(() => { this.letters.forEach((letter, index) => { setTimeout(function() { letter.classList.add('fill'); }, index * 125); }); }, 1000);` Use a fat arrow function to access `this` of parent scope. – Patrick Roberts Jun 02 '19 at 19:49
  • Damn -- that's it. I've read something about fat arrow functions and how they work well with `this`, but hadn't fully grasped it. I get it now. Thank you! – rpivovar Jun 02 '19 at 19:51
  • 1
    Not a duplicate, but see [What does “this” refer to in arrow functions in ES6?](https://stackoverflow.com/q/28371982/1541563) for more information. – Patrick Roberts Jun 02 '19 at 19:53
  • Thanks, taking a look – rpivovar Jun 02 '19 at 19:54

1 Answers1

0
    const letters = this.letters;

    letters.forEach((letter, index) => {
        setTimeout(() => {
            letter.classList.add('stroke');
        }, index * 125);
    });

    setTimeout(function() {
        letters.forEach((letter, index) => {
            setTimeout(() => {
                letter.classList.add('fill');
            }, index * 125);
        });
    }, 1000);

The issue is with the arrow (react uses es6 arrows as a way of binding this to functions).

setTimeout(function() { -> setTimeout(() => {

https://reactjs.org/docs/react-without-es6.html#autobinding

maxadorable
  • 1,290
  • 1
  • 10
  • 24