【问题标题】:How to trigger a function automatically when the data in an array changes?当数组中的数据发生变化时如何自动触发函数?
【发布时间】:2021-09-11 23:46:32
【问题描述】:

我刚刚开始使用 React js,我正在尝试使用 API 创建一个简单的食谱网站。

我正在尝试创建一个页面,该页面将使用 map() 从本地存储中的数组中显示最喜欢的食谱数据,如下所示。

const FavRecipes = () => {

    const [recipeArray, setRecipeArray] = useState([]);

    const refreshData = () => {
        const existedFavRecipe = localStorage.getItem("FavRecipes");
        const data = existedFavRecipe !== null ? JSON.parse(existedFavRecipe) : [];
        setRecipeArray(data);
    }

    return (
 
            <FavRecipesContainer>           
                {recipeArray.map( e => (
                    <>
                        <FavRecipeImage src ={e.image} />
                        <FavRecipeTitle>{e.title}</FavRecipeTitle>
                    </>
                ))}
            </FavRecipesContainer>

    )
}

问题是我希望refreshData 的功能在每次数组中的数据更改时自动触发,因为我将创建一个删除按钮来删除最喜欢的食谱。我正在考虑使用 useEffect() 但我不知道该怎么做。有什么建议可以解决这个问题吗?将不胜感激!

【问题讨论】:

  • 数组中的数据是什么意思?你想监听本地存储并在那时触发你的函数吗?
  • @KavinduVIndika 是的,如果可能的话,完全一样。实际上,我只是想在用户删除一些菜谱后检索最新的菜谱数组数据并自动显示给用户。

标签: javascript reactjs


【解决方案1】:

我希望refreshData 的功能在每次数组中的数据更改时自动触发,因为我将创建一个删除按钮,可以删除最喜欢的食谱..

问题是当本地存储被同一窗口中的其他代码更改时不会触发事件(storage 事件仅在其他窗口中更改存储时触发)。

有像these 这样的狡猾的解决方案,但实际上只是确保您的删除代码调用refreshData 作为其逻辑的一部分。或者实际上,您甚至不需要refreshData,您可以在组件中本地维护数组并将其回显到本地存储:

const FavRecipes = () => {
    const [recipeArray, setRecipeArray] = useState([]);

    // Initial data load
    useEffect(() => {
        const existedFavRecipe = localStorage.getItem("FavRecipes");
        const data = existedFavRecipe !== null ? JSON.parse(existedFavRecipe) : [];
        setRecipeArray(data);
    }, []);

    // Deletion
    const deleteRecipe = (recipe) => {
        setRecipeArray(recipes => {
            recipes = recipes.filter(r => r !== recipe);
            localStorage.setItem("FavRecipes", JSON.stringify(recipes));
            return recipes;
        });
    };

    return (
            <FavRecipesContainer>           
                {recipeArray.map( recipe => (
                    <>
                        <FavRecipeImage src ={recipe.image} />
                        <FavRecipeTitle>{recipe.title}</FavRecipeTitle>
                        <button onClick={() => deleteRecipe(recipe)}>X</button>
                    </>
                ))}
            </FavRecipesContainer>
    );
};

如果你还想监听其他窗口的变化(用户这样做):

const FavRecipesKey = "FavRecipes";
const FavRecipes = () => {
    const [recipeArray, setRecipeArray] = useState([]);

    // Initial data load and watch for changes in other windows
    useEffect(() => {
        function refreshData() {
            const existedFavRecipe = localStorage.getItem(FavRecipesKey);
            const data = existedFavRecipe !== null ? JSON.parse(existedFavRecipe) : [];
            setRecipeArray(data);
        }
        function storageEventHandler({key}) {
            if (key === FavRecipesKey) {
                refreshData();
            }
        }
        refreshData();
        window.addEventListener("storage", storageEventHandler);
        return () => {
            window.removeEventListener("storage", storageEventHandler);
        };
    }, []);

    // Deletion
    const deleteRecipe = (recipe) => {
        setRecipeArray(recipes => {
            recipes = recipes.filter(r => r !== recipe);
            localStorage.setItem(FavRecipesKey, JSON.stringify(recipes));
            return recipes;
        });
    };

    return (
            <FavRecipesContainer>           
                {recipeArray.map( recipe => (
                    <>
                        <FavRecipeImage src ={recipe.image} />
                        <FavRecipeTitle>{recipe.title}</FavRecipeTitle>
                        <button onClick={() => deleteRecipe(recipe)}>X</button>
                    </>
                ))}
            </FavRecipesContainer>
    );
};

【讨论】:

  • Crowder reciperecipesdeleteRecipe 函数中是什么?我很困惑
  • @Woppy - recipe 是被删除的配方(数组条目)。您可以看到我将其传递给() =&gt; deleteRecipe(recipe) 中的deleteRecipe,其中recipemap 回调的参数。
  • @Crowder 知道了!此方法有效且易于理解:)
【解决方案2】:

这是一个艰难的过程。 React 阵营中最接近的一个是useMutableSourcehttps://github.com/reactjs/rfcs/blob/main/text/0147-use-mutable-source.md

不过useMutableSource 有点太高级了。所以也许我们应该换一种方式来思考这个问题。例如,如果您可以知道调用localStorage.setItem 的时间或组件,那么您可以将其转换为上下文。

定义上下文

创建一个可以共享给其他组件的文件。

  const RecipeContext = React.createContext()
  export default RecipeContext

导入设置

当你要设置内容时,导入上下文并通过current写入。

  import RecipeContext from './RecipeContext'
  const AComponent = () => {
    const recipe = React.useContext(RecipeContext)
    recipe.current = recipeArray
  }

导入阅读

当你想读取当前值时,导入上下文并通过current读取。

  import RecipeContext from './RecipeContext'
  const BComponent = () => {
    const recipe = React.useContext(RecipeContext)
    const onClick = () => {
      console.log(recipe.current)
    }
  }

您应该能够将RecipeContext 用作类似于localStorage 的“全局”变量。如果你有任何默认值就更好了,你可以在创建它的时候设置它。

  const RecipeContext = React.createContext(defaultRecipeArray)

您甚至不需要提供者&lt;RecipeContext.Provider /&gt;,因为您将它用作“全局”上下文,这是一种非常特殊的用法。

【讨论】:

  • 他们将如何实现他们所说的删除? useContext 仅用于阅读上下文。
  • 删除就行recipe.current = null,你是直接管理存储,useContext是读取上下文,但是上下文本身可以通过current属性写入。
  • 这不会让任何东西重新渲染,不是吗?
  • 它可以,如果您有顶级提供商,RecipeContext.Provider value="" /&gt;。我以为你不需要它,但是如果你想要渲染,请使用提供者。
猜你喜欢
  • 1970-01-01
  • 2011-09-28
  • 2023-04-02
  • 1970-01-01
  • 2022-01-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多