Как передать функцию в state? Запоминается предыдущее значение

Всем привет!

Буду рад, если кто-то подскажет решение https://codesandbox.io/p/sandbox/function-in-state-does-not-work-8c9k5s

Сам пример - симуляция диалоговых окон с формами. То есть нажимаешь на одну кнопку - открывается одна форма с указанным onConfirm, на другую - другое действие.

Проблема в том, что renderComment запоминает comment на момент открытия этого самого окна и не изменяется при изменении инпута

Шаги:

  1. клик Open dialog 1
  2. ввожу в comment любое значение
  3. клик Confirm (comment пустой)
  4. клик Open dialog 2 (или 1, неважно)
  5. ввожу в comment другое значение
  6. клик Confirm - выводится значение, которое было после шага 4
const App = () => {
  const [comment, setComment] = useState("");
  const [func, setFunc] = useState(null);

  const Buttons = () => {
    const btns = [
      {
        title: "Open dialog 1",
        onConfirm: renderComment("one"),
      },
      {
        title: "Open dialog 2",
        onConfirm: renderComment("two"),
      },
    ];
    return btns.map(({ title, onConfirm }) => (
      <button key={title} onClick={() => setFunc(() => onConfirm)}>
        {title}
      </button>
    ));
  };

  const renderComment = (code) => () => {
    alert(code + " - " + comment);
  };

  return (
    <div>
      <Buttons />
      <br />
      {func ? (
        <>
          <input
            placeholder="comment"
            onChange={(e) => setComment(e.target.value)}
          />
          <p>Comment: {comment}</p>
          <button onClick={func}>Confirm</button>
        </>
      ) : null}
    </div>
  );
};

Ощущение, что я что-то базовое пропустил, но не пойму куда копать


Ответы (1 шт):

Автор решения: SwaD

Упускаете такую вещь, как замыкание. Вы функцию кладете в стейт из функции, где comment "пуст" и при каждом изменении состояния(ввод в инпут), функция пересоздается, но то что отправлено в стейт помнит, где жила.

Измените код вот так: Пусть renderComment возвращает функцию, которая принимает текущие комментарии:

const renderComment = (code) => (comment) => {
    alert(code + " - " + comment);
};

Ну и вызов фукнции будет выглядеть так:

<button onClick={() => func(comment)}>Confirm</button>

В целом, я бы рекомендовал не использовать такой подход, т.к. (ПРЕДПОЛОЖЕНИЕ) может быть утечка памяти из-за того, что ссылки на "старые" объекты хранятся. т.е. ссылка из старого компонента(перерисован уже много раз), и все что с ним было связано считается действующим и не будет удаляться сборщиком мусора.

P.S. Т.к. функция renderComment не имеет прямого отношения к текущему компоненту, ее можно вынести за его пределы

→ Ссылка