ReactJS: setTimeout () no funciona?

100

Teniendo este código en mente:

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')
);

¿No se supone que el estado cambiará solo después de 3 segundos? Está cambiando de inmediato.

Mi objetivo principal aquí es cambiar el estado cada 3 segundos (con setInterval()), pero como no estaba funcionando, lo intenté setTimeout(), que tampoco funciona. ¿Alguna luz sobre esto? ¡Gracias!

jbarradas
fuente
2
Si es foo(bar())así, barse ejecuta primero y se pasa su valor de retorno foo.
Felix Kling
@FelixKling eso parece correcto, pero no apropiado. Dado que foo()aquí es exactamente para ejecutar bardespués del tiempo de espera deseado. ¿O estoy completamente equivocado y se ejecuta de inmediato y solo devuelve el valor después del tiempo deseado?
jbarradas
3
"Dado que foo () aquí es exactamente para ejecutar la barra después del tiempo de espera deseado". Bien, es por eso que tienes que pasar bar, no llamarlo y pasar su valor devuelto. ¿Esperaba foo(bar())que cambiara el comportamiento de , dependiendo de lo que fooesté haciendo? Eso sería realmente extraño.
Felix Kling

Respuestas:

241

Hacer

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

De lo contrario, está pasando el resultado de setStatea setTimeout.

También puede utilizar las funciones de flecha de ES6 para evitar el uso de thispalabras clave:

setTimeout(
  () => this.setState({ position: 1 }), 
  3000
);
Daniel A. White
fuente
1
Sí, tiene sentido y está funcionando. Pero, ¿no es function () una función? Entonces, ¿por qué tendríamos que atarlo? Ya lo intenté y es realmente necesario, solo quería saber por qué.
Agradecemos
No entiendo por qué está diciendo que pasaría el resultado a setTimeout, ¿cómo no hace que esto funcione? ¿Cuál es el comportamiento en ese caso?
PositiveGuy
16
para aquellos de ustedes que prefieren usar las funciones de flecha de ES6: setTimeout(() => {this.setState({ position: 1 })}, 3000)@PositiveGuy no estoy seguro si ha investigado esto por su cuenta desde que se publicó esta pregunta, pero en caso de que no lo haya hecho: el ejemplo original de Daniel debe .bind(this)restringir el thiscontexto a setState; de lo contrario , thisautomáticamente se referirá al contexto en el que se invoca (en este caso, el anónimo al que functionse pasa setTimeout). Sin embargo, las funciones de flecha de ES6 tienen un ámbito léxico : se restringen thisal contexto en el que se llaman.
Zac Collier
1
No funciona ... setTimeout (() => {if (! This.props.logoIsLoading &&! This.props.isLoading) {console.log ('¿Pasaremos?'); This.setState ({.. .this.state, shouldUpdate: false, itemToUpdate: null, modalIsOpen: false, modalTitle: 'Add New Organization'});}}, 100); Está en el contexto de la clase de azúcar sintáctica de la clase Organizaciones extiende Componente {console.log nunca obtiene console.log ('¿Pasaremos?'); Todo lo anterior y posterior se registra.
juslintek
@juslintek define no funciona. haga una nueva pregunta si es necesario.
Daniel A. White
150
setTimeout(() => {
  this.setState({ position: 1 });
}, 3000);

Lo anterior también funcionaría porque la función de flecha ES6 no cambia el contexto de this.

Steven Scaffidi
fuente
3
La sintaxis de ES6 debería ser la respuesta aceptada para las mejores prácticas en React. Ambos funcionarán, pero este es más elegante y maneja this.
mccambridge
24

Cada vez que creamos un tiempo de espera, deberíamos borrarlo en componentWillUnmount, si aún no se ha disparado.

      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
fuente
11

Sé que esto es un poco antiguo, pero es importante notar que React recomienda borrar el intervalo cuando el componente se desmonta: https://reactjs.org/docs/state-and-lifecycle.html

Entonces me gusta agregar esta respuesta a esta discusión:

  componentDidMount() {
    this.timerID = setInterval(
      () => this.tick(),
      1000
    );
  }
  componentWillUnmount() {
    clearInterval(this.timerID);
  }
Fernando Lopes
fuente
8

setStatese invoca inmediatamente debido al paréntesis! Envuélvalo en una función anónima, luego llámelo:

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

No dijiste quién llamó a setTimeout

Aquí se explica cómo se llama al tiempo de espera sin llamar a funciones adicionales.

1. Puede hacer esto sin realizar funciones adicionales.

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

Utiliza function.prototype.bind ()

setTimeout toma la ubicación de la función y la mantiene en el contexto.

2. Otra forma de hacer lo mismo incluso escribiendo menos código.

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

Probablemente use el mismo método de enlace en algún momento

¿El setTimeout solo toma la ubicación de la función y la función ya tiene el contexto? De todos modos, ¡funciona!

NOTA: Estos funcionan con cualquier función que use en js.

Aconsejar
fuente
5

Su alcance de código ( this) será su windowobjeto, no su componente de reacción, y es por eso que setTimeout(this.setState({position: 1}), 3000)se bloqueará de esta manera.

Eso viene de javascript, no de React, es un cierre de js


Entonces, para vincular el alcance del componente de reacción actual, haga esto:

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

O si su navegador es compatible con es6 o sus proyectos son compatibles con la compilación de es6 a es5, pruebe también la función de flecha, ya que la función de flecha es para solucionar 'este' problema:

setTimeout(()=>this.setState({position: 1}), 3000);
Xin
fuente
3

Hay 3 formas de acceder al alcance dentro de la función 'setTimeout'

Primero,

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

El segundo es usar la función de flecha ES6, porque la función de flecha no tenía alcance (esto)

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

El tercero es vincular el alcance dentro de la función

setTimeout(function(){
   this.setState({position:1})
}.bind(this), 3000)
Darryl Fabian
fuente
1

Hizo un error de declaración de sintaxis, use la declaración setTimeout adecuada

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