Странное поведение ловушек React: отложенное обновление данных - PullRequest
0 голосов
/ 04 мая 2019

Странное поведение: я ожидаю, что первый и второй console.log отображают разные результаты, но они показывают тот же результат, и значение изменяется только при следующем щелчке. Как мне отредактировать мой код, чтобы значение было другим?

function App() {
  const [count, setCount] = React.useState(false);
  const test = () => {
    console.log(count); //false
    setCount(!count);
    console.log(count); //false
  };
  return (
    <div className="App">
      <h1 onClick={test}>Hello StackOverflow</h1>
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="root"></div>

Вы можете увидеть рабочий пример здесь: https://codesandbox.io/s/wz9y5lqyzk

Ответы [ 3 ]

3 голосов
/ 04 мая 2019

Хук setState не обновляет значение в компоненте напрямую.Он обновляет свое внутреннее состояние, вызывает повторную визуализацию компонента, а затем возвращает новое значение состояния, которое вы присваиваете count.

Когда вы console.log(count) внутри test, вы отображаете текущийзначение count, которое является старым значением (до обновления).Если вы переместите console.log() на рендер, вы увидите новое значение:

const { useState } = React;

const Component = () => {
  const [count, setCount] = useState(false);

  const test = () => {
    console.log('before: ', count);
    setCount(!count)
  }
  
  console.log('after: ', count);

  return (
    <div className="App">
      <button onClick={test}>Click</button>
    </div>
  );
}

ReactDOM.render(
   <Component />,
   demo
);
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id="demo"></div>
2 голосов
/ 04 мая 2019

Обновление состояния является асинхронным, поэтому, если вы используете хуки, вы можете использовать useEffect, чтобы получать уведомления после обновления.

Вот пример:

https://codesandbox.io/s/wxy342m6l

Если вы используете setState компонента React, вы можете использовать обратный вызов

this.setState((state) => ({count: !state.count}), () => console.log('Updated', this.state.count));

Не забудьте использовать обратный вызов для обновления состояния, если вам нужно значение состояния.

2 голосов
/ 04 мая 2019

Обновления состояния асинхронные .Функция setCount, которую вы получаете от useState, не может волшебным образом протянуть руку и изменить значение вашей count константы.С одной стороны, это константа.С другой стороны, setCount не имеет к нему доступа.Вместо этого, когда вы вызываете setCount, функция компонента будет вызвана снова, а useState вернет обновленный счетчик.

Live Пример:

const {useState} = React;

function Example() {
  const [count, setCount] = useState(false);
  const test = () =>{
    setCount(!count); // Schedules asynchronous call to Example, re-rendering the component
  };
  return (
    <div className="App">
      <h1 onClick={test}>Hello CodeSandbox</h1>
      <div>Count: {String(count)}</div>
    </div>
  );
}

ReactDOM.render(<Example />, document.getElementById("root"));
<div id="root"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>

Если вам необходимо выполнить побочный эффект при изменении count, используйте useEffect:

useEffect(() => {
  console.log(`count changed to ${count}`);
}, [count]);

Обратите внимание, что мы говорим useEffect, что мы должны вызывать наш обратный вызов только при изменении count, поэтому, если у нас есть другое состояние, наш обратный вызов не запускается без необходимости.

Live Пример:

const {useState, useEffect} = React;

function Example() {
  const [count, setCount] = useState(false);
  const test = () =>{
    setCount(!count); // Schedules asynchronous call to Example, re-rendering the component
  };
  useEffect(() => {
    console.log(`count changed to ${count}`);
  }, [count]);
  return (
    <div className="App">
      <h1 onClick={test}>Hello CodeSandbox</h1>
      <div>Count: {String(count)}</div>
    </div>
  );
}

ReactDOM.render(<Example />, document.getElementById("root"));
<div id="root"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
...