Реагировать: доступ к (обновленному) состоянию с помощью useState: он не обновляется внутри компонента, который его создает, но находится за пределами того места, где он вызывается

Почему у меня нет доступа к обновленному значению recipes (useState) изнутри компонента, который его определяет?

В этом примере вы можете увидеть, как отсутствие доступа к этому значению вызывает ошибку в приложении после удаления ссылки на функцию, которую я использую для обновления состояния.

=> Кодовое поле и код ниже

* Дважды щелкните <h1>, чтобы увидеть ошибку

https://codesandbox.io/s/sparkling-sea-5iqgo?fontsize=14&hidenavigation=1&theme=dark

import React, { useEffect, useState } from "react";

export default function App() {
  const [userRecipes, setUserRecipes] = useRecipesData();

  return (
    <div className="App">
      <h1
        onClick={() => {
          userRecipes.setBookmarks("onetwothree");
        }}
      >
        Hello CodeSandbox
      </h1>
      <h2>{userRecipes.bookmarked_recipes}</h2>
    </div>
  );
}

const useRecipesData = () => {
  const [recipes, setRecipes] = useState({});

  const setBookmarks = newRecipes => {
    console.log(recipes); // is undefined !? and deletes setBookmarks
    setRecipes({
      bookmarked_recipes: newRecipes,
      setBookmarks: recipes.setBookmarks
    });
  };

  useEffect(() => {
    setRecipes({
      bookmarked_recipes: "testtesttest",
      setBookmarks: setBookmarks
    });
  }, []);

  return [recipes, setRecipes];
};

Я не понимаю, почему, если я верну [recipes, setRecipes], где recipes.setBookmarks хранит ссылку на функцию, это не сработает.

Но если я верну саму функцию (которая также является ссылкой) [recipes, setBookmarks], тогда она будет работать

Посмотрите этот другой код, где он работает

https://codesandbox.io/s/red-violet-gju99?fontsize=14&hidenavigation=1&theme=dark

import React, { useEffect, useState } from "react";
import "./styles.css";

export default function App() {
  const [userRecipes, setUserRecipes] = useRecipesData();

  return (
    <div className="App">
      <h1
        onClick={() => {
          setUserRecipes("onetwothree" + Math.random());
        }}
      >
        Hello CodeSandbox
      </h1>
      <h2>{userRecipes.bookmarked_recipes}</h2>
    </div>
  );
}

const useRecipesData = () => {
  const [recipes, setRecipes] = useState({});

  const setBookmarks = newRecipes => {
    console.log(recipes); // is defined this time
    setRecipes({
      bookmarked_recipes: newRecipes,
      setBookmarks: recipes.setBookmarks
    });
  };

  useEffect(() => {
    setRecipes({
      bookmarked_recipes: "testtesttest",
      setBookmarks: setBookmarks
    });
  }, []);

  return [recipes, setBookmarks];
};

person GWorking    schedule 15.01.2020    source источник


Ответы (1)


Все дело в контексте. Если вы введете console.log(receipes) в useEffect и саму функцию рендеринга, вы сможете увидеть, каков поток событий:

  1. Рецепт первого рендеринга пуст.
  2. Вызывается UseEffect и помещает setBookmark в рецепт (но рецепт для setBookmark пуст)
  3. Вызывается второй рендеринг, и теперь в рецепте есть "testesttest", а recipe.setBookmark - это функция, в которой привязанный к ней объект рецепта является значением рецепта из события 1.
  4. setBookmark вызывается, recipe теперь установлен в onetwothree, но объект recipe пуст, поэтому мы устанавливаем setBookmark на undefined.

вместо того, чтобы сохранять функцию внутри состояния, вам нужно просто вызвать ее напрямую (например, вернуть setBookmark, а не setRecipes, например:

import React, { useEffect, useState } from "react";
import "./styles.css";

export default function App() {
  const [userRecipes, setBookmarks] = useRecipesData();

  return (
    <div className="App">
      <h1
        onClick={() => {
          setBookmarks("onetwothree" + Math.random());
        }}
      >
        Hello CodeSandbox
      </h1>
      <h2>{userRecipes.bookmarked_recipes}</h2>
    </div>
  );
}

const useRecipesData = () => {
  const [recipes, setRecipes] = useState({});
  const setBookmarks = newRecipes => {
    setRecipes({
      bookmarked_recipes: newRecipes,
    });
  };

  useEffect(() => {
    setRecipes({
      bookmarked_recipes: "testtesttest",
    });
  }, []);

  return [recipes, setBookmarks];
};
person Alon Bar David    schedule 15.01.2020
comment
Я не понимаю, почему при возврате setBookmark (ссылка) console.log(recipes) работает, но возвращает ту же функцию в переменной (потому что это также ссылка), console.log(recipes) получает не обновленный recipes (я переписываю вопрос, чтобы показать вам код, о котором я говорю) - person GWorking; 15.01.2020