React setState не оновлює стан


91

Отже, у мене таке:

let total = newDealersDeckTotal.reduce(function(a, b) {
  return a + b;
},
0);

console.log(total, 'tittal'); //outputs correct total
setTimeout(() => {
  this.setState({dealersOverallTotal: total});
}, 10);

console.log(this.state.dealersOverallTotal, 'dealersOverallTotal1'); //outputs incorrect total

newDealersDeckTotal - це просто масив чисел, [1, 5, 9]наприклад, однак this.state.dealersOverallTotalне дає правильної суми, але totalробить? Я навіть затримав тайм-аут, щоб побачити, чи це вирішило проблему. будь-який очевидний або я повинен розмістити більше коду?



@Assan ура!
Черв’як

Окрім того, що сказано у відповідях, ви чітко реєструєте значення стану, перш ніж телефонувати setState.
Фелікс Клінг

1
@FelixKling ні, я називаю this.state після того, як я його встановив. Я реєструю змінну раніше. немає?
Черв’як

Через час очікування ваш setStateдійсно виконується після реєстрації стану. Я думаю, що під час налагодження ви мали намір помістити console.logчастину всередині тайм-ауту та setStateзовні.
Фабіан Шульц

Відповіді:


182

setState()як правило, є асинхронним, що означає, що на той час, коли ви console.logє штатом, він ще не оновлений. Спробуйте помістити журнал у зворотний виклик setState()методу. Він виконується після завершення зміни стану:

this.setState({ dealersOverallTotal: total }, () => {
  console.log(this.state.dealersOverallTotal, 'dealersOverallTotal1');
}); 

1
На додаток до цього, OP чітко реєструє значення стану перед їх викликом setState.
Фелікс Клінг

Це працює і для мене, раніше я використовував це: `this.setState ({someVar: newValue}, function () {console.log (" force update}); 'але з якихось причин це не хвилювало більше, коли я оновив код, як описано вище, він працює. будь-яка ідея чому?
Jozcar

@Jozcar Також повинен працювати, синтаксис був неправильним (відсутні дужки):this.setState({someVar: newValue},function(){ console.log("force update") });
Фабіан Шульц

imgur.com/Ku0OjTl Будь ласка, скажіть мені, що мені робити, щоб позбутися цієї проблеми.
Джонсі

Це не працює, якщо ви використовуєте useStateхук у функціональному компоненті. Використовуйте useEffectзамість цього для ефекту після відтворення.
Хасан Сефа Озалп

16

setState є асинхронним. Ви можете використовувати метод зворотного виклику, щоб отримати оновлений стан.

changeHandler(event) {
    this.setState({ yourName: event.target.value }, () => 
    console.log(this.state.yourName));
 }

10

Використання async / await

async changeHandler(event) {
    await this.setState({ yourName: event.target.value });
    console.log(this.state.yourName);
}

8

setState()Операція асинхронна і , отже , ваш console.log()буде виконуватися до setState()мутує значення і , отже , ви бачите результат.

Щоб її вирішити, зафіксуйте значення у функції зворотного виклику setState(), наприклад:

setTimeout(() => {
    this.setState({dealersOverallTotal: total},
    function(){
       console.log(this.state.dealersOverallTotal, 'dealersOverallTotal1');
    });
}, 10)

JavaScript завжди синхронний.
Сантош Сінгх,

1
@santoshsingh у вас помилкова думка. Виклики API, тайм-аути відбуваються асинхронно.
Шубхем Хатрі

Як ви вже згадували вище, що javascript є асинхронним - це не правильно. Це переважно синхронно, а для випадків це асинхронно. stackoverflow.com/questions/2035645/…
Сантош Сінгх

@santoshsingh. Ох, це була помилка з мого боку.
Шубхам Хатрі

дуже розумне використання зворотного дзвінка, щоб забезпечити оновлення стану перед наступним дзвінком!
user1204214

5

У випадку з гачками, слід використовувати useEffectгачок.

const [fruit, setFruit] = useState('');

setFruit('Apple');

useEffect(() => {
  console.log('Fruit', fruit);
}, [fruit])

Чудово, це працює з useEffect. Але навіщо це потрібно?
alex351

useEffectвиконується при кожному повторному відтворенні, і якщо елементи, що передаються в масив, є змінними стану, пісок змінюється. Отже, коли фрукт зміниться і компонент повторно відтвориться, цей useEffect буде працювати.
Сірадж Алам

Я запускаю setFruit і console.log fruit поза useEffect, і він не змінюється. : /
alex351

3

Settate є асинхронним у відповідь, тому, щоб побачити оновлений стан у консолі, використовуйте зворотний виклик, як показано нижче (функція зворотного виклику буде виконана після оновлення setstate)

введіть тут опис зображення

Наведений нижче метод "не рекомендується", але для розуміння, якщо ви безпосередньо мутуєте стан, ви зможете побачити оновлений стан у наступному рядку. Повторюю, це "не рекомендується"

введіть тут опис зображення


ДЯКУЮ, це все, вам потрібно безпосередньо встановити змінну
notacorn


0

У мене виникла проблема з налаштуванням стану реагування кілька разів (він завжди використовував стан за замовчуванням). Після цієї проблеми з реакцією / github у мене спрацювало

const [state, setState] = useState({
  foo: "abc",
  bar: 123
});

// Do this!
setState(prevState => {
  return {
    ...prevState,
    foo: "def"
  };
});
setState(prevState => {
  return {
    ...prevState,
    bar: 456
  };
});

0

У мене була така сама ситуація з деяким заплутаним кодом, і нічого з існуючих пропозицій не спрацювало для мене.

Моєю проблемою було те, що setStateвідбувалося з функції зворотного виклику, виданої одним із компонентів. І мені підозріло, що дзвінок відбувався синхронно, що setStateвзагалі заважало встановити стан.

Простіше кажучи, у мене є щось подібне:

render() {
    <Control
        ref={_ => this.control = _}
        onChange={this.handleChange}
        onUpdated={this.handleUpdate} />
}

handleChange() {
    this.control.doUpdate();
}

handleUpdate() {
    this.setState({...});
}

Як я повинен був «виправити» це було покласти doUpdate()в setTimeoutтак:

handleChange() {
    setTimeout(() => { this.control.doUpdate(); }, 10);
}

Не ідеально, але в іншому випадку це буде суттєвий рефакторинг.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.