React.useCallback не обновляется с зависимостью - PullRequest
0 голосов
/ 05 августа 2020

Я использую следующий компонент для отправки комментариев в своем приложении:

const App = () => {
    const [text, setText] = React.useState("");

    const send = React.useCallback(() => {
        setText("");
        console.log("sending", text);
    }, [text]);

    React.useEffect(() => {
        const handler = e => {
            switch (e.keyCode) {
                case 13: // enter
                    if (e.shiftKey) {
                        e.preventDefault();
                        send();
                    }
                    break;
            }
        }
        
        document.addEventListener("keydown", handler);
        return () => document.removeEventListener("keydown", handler);
    }, []);

    return <div className="App">
        <textarea
            className="App__text"
            value={text}
            onChange={e => {
                setText(e.target.value);
            }} />
        <button className="App__send" onClick={send}>send</button>
    </div>;
};

здесь работает демонстрация

Это простой текстовое поле и кнопка. При нажатии кнопки или Shift-Enter текст в текстовом поле отправляется на сервер (здесь мы просто console.log это).

Кнопка работает нормально - введите "hello world" (или что угодно) нажмите кнопку, и консоль скажет hello world.

Shift-enter, однако, всегда печатает пустую строку.

Полагаю, я Недоразумение useCallback. Насколько я понимаю, useCallback оборачивает вашу функцию. Когда одна из зависимостей изменяется, React заменяет вашу функцию, не меняя функцию-оболочку, поэтому она остается актуальной ссылкой, где бы она ни использовалась. Учитывая, что send, вызываемый в useEffect, кажется, имеет начальное значение text в области видимости, но тот, который используется в onClick из button, имеет новейшее значение, мои предположения кажутся неверными.

Я также пробовал добавить text в качестве зависимости от useEffect, но

  • Я не хочу удалять / создавать / добавлять слушателя при каждом нажатии клавиши и
  • Все равно отсутствует последний нажатый символ.

Как сохранить текущую версию моей send функции внутри другого обратного вызова?

1 Ответ

2 голосов
/ 05 августа 2020

Вам не хватает send в ваших зависимостях, взгляните на этот обновленный код:

React.useEffect(() => {
  const handler = e => {
    switch (e.keyCode) {
      case 13: // enter
        if (e.shiftKey) {
          e.preventDefault();
          send(); // This will always be called an older version, instead of updated one
        }
        break;
    }
  }

  document.addEventListener("keydown", handler);
  return () => document.removeEventListener("keydown", handler);
}, [send]); // You need to put send here, since it is part of this component, and needs to be updated

Причина, по которой он работает с ним, заключается в том, что функция send также запоминается:

const send = React.useCallback(() => {
  setText("");
  console.log("sending", text);
}, [text]);

, поэтому вам нужно обязательно обновить его до более новой версии с более новым текстом (чего никогда не было, поэтому у вас нет текста в вашем SHIFT + ENTER)

РЕДАКТИРОВАТЬ: После дальнейшего расследования мне кажется, что самая большая проблема заключалась в рассинхронизации c текста и обработчика слушателя .

Я изменил код для работы, полностью удалив слушателя, и используя onKeyDown prop прямо в текстовом поле. Взгляните на этот рабочий код:

https://codepen.io/antonioerda/pen/zYqYWgx

...