CustomHook: избегайте использования старой области в обратном вызове - PullRequest
0 голосов
/ 07 мая 2020

Я новичок в React, и я создал минимальное тестовое приложение, чтобы проиллюстрировать проблему scope.

import React, { useState, useEffect } from 'react';
import logo from './logo.svg';
import './App.css';


function useCounter(topic, defaultMsg) {
  const [message, setMessage] = useState(defaultMsg);

  const increment=() =>{
      console.log("increment from " + message);
      setMessage(message+1);
  }

  return [message, increment];
}

export default function App() {
  const [counter, incrementCounter] = useCounter("anyData", 0);


  return (
    <div>
      <p>you clicked: {counter} times</p>

      <button
        onClick={() => {
          console.log("sending ");
          incrementCounter(counter); //works 
          incrementCounter(counter); //doesn't work because using the old scope
        }}
      >
        Click me
      </button>
    </div>
  );
}

Вывод

App.js:10 increment from 0
App.js:10 increment from 0  //should be 1
App.js:27 sending 
App.js:10 increment from 1 
App.js:10 increment from 1  //should be 2

Если кнопка нажата, затем incrementCounter вызывается дважды. Ожидаемое поведение: каждый щелчок увеличивает счетчик на 2. Однако этого не происходит, потому что хук useCounter, похоже, использует старое значение сообщения. Таким образом, каждый щелчок увеличивает счетчик на 1. Я знаю, почему это не работает, я не знаю, как это исправить.

Как бы вы решили эту проблему?

1 Ответ

1 голос
/ 07 мая 2020

Проблема в том, что вызовы обновления состояния в обработчиках событий группируются, и, следовательно, несколько вызовов setState без использования здесь обратного вызова не обновят значение правильно.

as

 setMessage(message + 1);
 setMessage(message + 1);

Теперь оба этих вызова объединены, и последнее изменение, которое будет применено к setState, - message + 1, которое 1.

Также обновления состояния являются асинхронными c и не отражаются немедленно

Если вы измените реализацию setMessage на function state update

setMessage(prevMessage => prevMessage+1);

Вы увидите ожидаемый результат, потому что в этом случае пакетирование не выполняется с помощью реакции, и состояние будет увеличено дважды. Однако вы все равно будете видеть тот же вывод в консоли из-за асинхронной c природы setState

Sample DEMO

...