ReactJS: setTimeout () не работает?

102

Имея в виду этот код:

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

Разве состояние не должно измениться только через 3 секунды? Это сразу меняется.

Моя основная цель здесь - менять состояние каждые 3 секунды (с setInterval()), но, поскольку это не работало, я попробовал setTimeout(), что тоже не работает. Есть свет на этом? Спасибо!

Jbarradas
источник
2
Если у вас есть , foo(bar())то barбудет выполнен первый и ее возвращаемое значение передается foo.
Felix Kling
@FelixKling, это кажется правильным, но неуместным. Поскольку foo()здесь точно выполняется barпосле желаемого тайм-аута. Или я совершенно не прав, и он выполняется сразу, а возвращает значение только через желаемое время?
jbarradas
3
«Так как здесь foo () выполняет bar после желаемого тайм-аута». Правильно, поэтому вы должны передавать bar, а не вызывать его и передавать возвращаемое значение. Вы ожидали, что поведение пользователя foo(bar())изменится в зависимости от того, что fooон делает? Это было бы действительно странно.
Felix Kling

Ответы:

245

Делать

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

В противном случае вы передаете результат setStateв setTimeout.

Вы также можете использовать стрелочные функции ES6, чтобы избежать использования thisключевого слова:

setTimeout(
  () => this.setState({ position: 1 }), 
  3000
);
Дэниел А. Уайт
источник
1
Да, имеет смысл, и это работает. Но разве function () не является функцией? Так зачем нам его связывать? Я уже пробовал и это действительно нужно, я просто хотел знать, почему. Благодарю за помощь :)
jbarradas
Я не понимаю, почему вы говорите, что результат будет передан в setTimeout, как это не работает? Каково поведение в этом случае?
PositiveGuy
16
для тех из вас, кто предпочитает использовать стрелочные функции setTimeout(() => {this.setState({ position: 1 })}, 3000)ES6 : @PositiveGuy не уверен, исследовали ли вы это самостоятельно с тех пор, как этот вопрос был опубликован, но если вы этого не сделали: исходный пример Даниэля должен .bind(this)ограничить thisконтекст setState- иначе , thisбудет автоматически ссылаться на контекст, в котором он вызывается (в данном случае, на анонимный functionобъект setTimeout). Однако стрелочные функции ES6 имеют лексическую область видимости - они ограничиваются thisконтекстом, в котором они вызываются.
Zac Collier
1
Не работает ... setTimeout (() => {if (! This.props.logoIsLoading &&! This.props.isLoading) {console.log ('Произойдет ли?'); This.setState ({.. .this.state, shouldUpdate: false, itemToUpdate: null, modalIsOpen: false, modalTitle: 'Добавить новую организацию'});}}, 100); Это в контексте синтаксического сахара класса. Класс Организации расширяет Компонент {console.log никогда не получает console.log ('Произойдет ли это?'); Все, что было до и после него, записывается.
juslintek
@juslintek определить не работает. при необходимости задайте новый вопрос.
Daniel A. White
150
setTimeout(() => {
  this.setState({ position: 1 });
}, 3000);

Вышеупомянутое также будет работать, потому что функция стрелки ES6 не меняет контекст this.

Стивен Скаффиди
источник
3
Синтаксис ES6 должен быть принятым ответом на лучшие практики в React. Оба будут работать, но это более элегантно и удобно this.
McCambridge 01
24

Каждый раз, когда мы создаем тайм-аут, мы должны очистить его для componentWillUnmount, если он еще не сработал.

      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')
);
Халид Азам
источник
11

Я знаю, что это немного устарело, но важно отметить, что React рекомендует очищать интервал, когда компонент отключается: https://reactjs.org/docs/state-and-lifecycle.html

Поэтому я хотел бы добавить этот ответ к этому обсуждению:

  componentDidMount() {
    this.timerID = setInterval(
      () => this.tick(),
      1000
    );
  }
  componentWillUnmount() {
    clearInterval(this.timerID);
  }
Фернандо Лопес
источник
8

setStateвызывается немедленно из-за скобок! Оберните его анонимной функцией, а затем вызовите:

setTimeout(function() {
    this.setState({position: 1})
}.bind(this), 3000);
tymeJV
источник
6

Вы не сказали, кто вызвал setTimeout

Вот как вы вызываете тайм-аут без вызова дополнительных функций.

1. Вы можете сделать это без дополнительных функций.

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

Использует function.prototype.bind ()

setTimeout принимает местоположение функции и сохраняет его в контексте.

2. Другой способ сделать то же самое, даже написав еще меньше кода.

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

Возможно, в какой-то момент использует тот же метод привязки

SetTimeout принимает только местоположение функции, а у функции уже есть контекст? Во всяком случае, работает!

ПРИМЕЧАНИЕ. Они работают с любой функцией, которую вы используете в js.

Советник
источник
5

Ваш код scope ( this) будет вашим windowобъектом, а не вашим реагирующим компонентом, и именно поэтому он setTimeout(this.setState({position: 1}), 3000)выйдет из строя.

Это исходит из javascript, а не из React, это закрытие js


Итак, чтобы привязать текущую область действия компонента реакции, сделайте следующее:

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

Или, если ваш браузер поддерживает es6 или ваши проекты поддерживают компиляцию es6 в es5, попробуйте также стрелочную функцию, так как стрелочная функция предназначена для исправления этой проблемы:

setTimeout(()=>this.setState({position: 1}), 3000);
Синь
источник
3

Есть 3 способа получить доступ к области внутри функции setTimeout

Первый,

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

Во-вторых, использовать стрелочную функцию ES6, потому что стрелочная функция не имеет области действия (это)

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

Третий - привязать область видимости внутри функции

setTimeout(function(){
   this.setState({position:1})
}.bind(this), 3000)
Дэррил Фабиан
источник
1

Вы сделали ошибку объявления синтаксиса, используйте правильное объявление setTimeout

message:() => { 
  setTimeout(() => {this.setState({opened:false})},3000); 
  return 'Thanks for your time, have a nice day 😊! 
}
КАРТИКЕЯН А.
источник
0

Попробуйте использовать синтаксис ES6 установленного тайм-аута. Обычный javascript setTimeout () не будет работать в React JS

setTimeout(
      () => this.setState({ position: 100 }), 
      5000
    );
Codemaker
источник