ReactJS: setTimeout () nie działa?

102

Mając na uwadze ten kod:

var Component = React.createClass({

    getInitialState: function () {
        return {position: 0};    
    },

    componentDidMount: function () {
        setTimeout(this.setState({position: 1}), 3000);
    },

    render: function () {
         return (
            <div className="component">
                {this.state.position}
            </div>
         ); 
    }

});

ReactDOM.render(
    <Component />,
    document.getElementById('main')
);

Czy stan nie powinien się zmieniać dopiero po 3 sekundach? To się zmienia natychmiast.

Moim głównym celem jest tutaj zmiana stanu co 3 sekundy (z setInterval()), ale ponieważ nie działała, próbowałem setTimeout(), która też nie działa. Jakieś światła na tym? Dzięki!

jbarradas
źródło
2
Jeśli masz foo(bar())to barjest wykonywany jako pierwszy, a jego wartość zwracana jest przekazywana do foo.
Felix Kling
@FelixKling, który wydaje się poprawny, ale niewłaściwy. Ponieważ foo()tutaj jest dokładnie do wykonania barpo żądanym czasie. A może całkowicie się mylę i jest wykonywany od razu i zwraca wartość dopiero po zadanym czasie?
jbarradas
3
„Ponieważ foo () ma tutaj dokładnie wykonać pasek po żądanym czasie oczekiwania”. Właśnie dlatego musisz przekazać bar, a nie wywoływać go i przekazywać jego wartość zwrotną. Czy spodziewałeś się, że zachowanie foo(bar())się zmieni, w zależności od tego, co foorobi? To byłoby naprawdę dziwne.
Felix Kling

Odpowiedzi:

245

Robić

setTimeout(
    function() {
        this.setState({ position: 1 });
    }
    .bind(this),
    3000
);

W przeciwnym razie przekazujesz wynik setStatedo setTimeout.

Możesz także użyć funkcji strzałek ES6, aby uniknąć użycia thissłowa kluczowego:

setTimeout(
  () => this.setState({ position: 1 }), 
  3000
);
Daniel A. White
źródło
1
Tak, ma sens i działa. Ale czy function () nie jest funkcją? Więc dlaczego mielibyśmy to wiązać? Już próbowałem i jest to naprawdę potrzebne, chciałem tylko wiedzieć dlaczego.
Wdzięczni
Nie rozumiem, dlaczego twierdzisz, że wynik przeszedłby do setTimeout, dlaczego to nie działa? Jakie jest zachowanie w tym przypadku?
PositiveGuy
16
dla tych z Was, którzy wolą używać funkcji strzałek ES6: setTimeout(() => {this.setState({ position: 1 })}, 3000)@PositiveGuy nie jestem pewien, czy zbadałeś to samodzielnie od czasu opublikowania tego pytania, ale na wypadek, gdybyś tego nie zrobił: oryginalny przykład Daniela musi .bind(this)ograniczyć thiskontekst do setState- w przeciwnym razie , thisautomatycznie odnoszą się do kontekstu, w którym jest on wywoływany (w tym przypadku anonimowość functionbyły przekazywane do setTimeout). Funkcje strzałkowe ES6 mają jednak zasięg leksykalny - ograniczają thissię do kontekstu, w którym są wywoływane.
Zac Collier
1
Nie działa ... setTimeout (() => {if (! This.props.logoIsLoading &&! This.props.isLoading) {console.log ('Czy to się stanie?'); This.setState ({.. .this.state, shouldUpdate: false, itemToUpdate: null, modalIsOpen: false, modalTitle: 'Dodaj nową organizację'});}}, 100); Jest to w kontekście składniowej klasy cukru, klasa Organizacje rozszerza komponent {console.log nigdy nie otrzyma console.log ('Czy się wydarzy?'); Wszystko przed i po tym jest rejestrowane.
juslintek
@juslintek zdefiniować nie działa. w razie potrzeby zadaj nowe pytanie.
Daniel A. White
150
setTimeout(() => {
  this.setState({ position: 1 });
}, 3000);

Powyższe działałoby również, ponieważ funkcja strzałki ES6 nie zmienia kontekstu this.

Steven Scaffidi
źródło
3
Składnia ES6 powinna być akceptowaną odpowiedzią na najlepsze praktyki w React. Oba będą działać, ale to jest bardziej eleganckie i uchwyty this.
mccambridge
24

Za każdym razem, gdy tworzymy limit czasu, powinniśmy wyczyścić go na componentWillUnmount, jeśli jeszcze nie został uruchomiony.

      let myVar;
         const Component = React.createClass({

            getInitialState: function () {
                return {position: 0};    
            },

            componentDidMount: function () {
                 myVar = setTimeout(()=> this.setState({position: 1}), 3000)
            },

            componentWillUnmount: () => {
              clearTimeout(myVar);
             };
            render: function () {
                 return (
                    <div className="component">
                        {this.state.position}
                    </div>
                 ); 
            }

        });

ReactDOM.render(
    <Component />,
    document.getElementById('main')
);
Khalid Azam
źródło
11

Wiem, że jest to trochę stare, ale ważne jest, aby zauważyć, że React zaleca wyczyszczenie interwału po odinstalowaniu komponentu: https://reactjs.org/docs/state-and-lifecycle.html

Więc chciałbym dodać tę odpowiedź do tej dyskusji:

  componentDidMount() {
    this.timerID = setInterval(
      () => this.tick(),
      1000
    );
  }
  componentWillUnmount() {
    clearInterval(this.timerID);
  }
Fernando Lopes
źródło
8

setStatejest wywoływana natychmiast z powodu nawiasów! Owiń to w anonimową funkcję, a następnie nazwij ją:

setTimeout(function() {
    this.setState({position: 1})
}.bind(this), 3000);
tymeJV
źródło
6

Nie powiedziałeś, kto dzwonił do setTimeout

Tutaj możesz wywołać limit czasu bez wywoływania dodatkowych funkcji.

1. Możesz to zrobić bez wykonywania dodatkowych funkcji.

setTimeout(this.setState.bind(this, {position:1}), 3000);

Używa function.prototype.bind ()

setTimeout przyjmuje lokalizację funkcji i utrzymuje ją w kontekście.

2. Innym sposobem, aby zrobić to samo, nawet pisząc jeszcze mniej kodu.

setTimeout(this.setState, 3000, {position:1});

Prawdopodobnie w pewnym momencie używa tej samej metody wiązania

SetTimeout przyjmuje tylko lokalizację funkcji, a funkcja ma już kontekst? W każdym razie działa!

UWAGA: Działają one z każdą funkcją używaną w js.

Doradzić
źródło
5

Zasięg kodu ( this) będzie twoim windowobiektem, a nie komponentem reagowania, i dlatego setTimeout(this.setState({position: 1}), 3000)ulegnie awarii w ten sposób.

To pochodzi z javascript, a nie z React, jest to zamknięcie js


Tak więc, aby powiązać obecny zakres komponentu React, wykonaj następujące czynności:

setTimeout(function(){this.setState({position: 1})}.bind(this), 3000);

Lub jeśli twoja przeglądarka obsługuje es6 lub twoje projs obsługują kompilację es6 do es5, wypróbuj również funkcję strzałki, ponieważ strzałka func ma rozwiązać ten problem:

setTimeout(()=>this.setState({position: 1}), 3000);
Xin
źródło
3

Istnieją 3 sposoby uzyskania dostępu do zakresu wewnątrz funkcji „setTimeout”

Pierwszy,

const self = this
setTimeout(function() {
  self.setState({position:1})
}, 3000)

Po drugie, użyj funkcji strzałkowej ES6, ponieważ funkcja strzałkowa nie miała swojego zakresu (to)

setTimeout(()=> {
   this.setState({position:1})
}, 3000)

Trzecim jest powiązanie zakresu wewnątrz funkcji

setTimeout(function(){
   this.setState({position:1})
}.bind(this), 3000)
Darryl Fabian
źródło
1

Wystąpił błąd w deklaracji składni, użyj właściwej deklaracji setTimeout

message:() => { 
  setTimeout(() => {this.setState({opened:false})},3000); 
  return 'Thanks for your time, have a nice day 😊! 
}
KARTHIKEYAN.A
źródło