How to trigger a function automatically when the data in an array changes?

i just started React js and im trying to create a simple recipe web with API.

I am trying to create a page that will display the data of favorite recipes from an array in local storage using map() like below.

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>

    )
}

The problem is I want the function of refreshData to get triggered automatically everytime the data in the array changes because i will create a delete button that can delete the favorite recipes. I am thinking of using useEffect() but I dont know how to do it. Is there any suggestion to solve this? Would appreciate it!

Answer

I want the function of refreshData to get triggered automatically everytime the data in the array changes because i will create a delete button that can delete the favorite recipes..

The problem is that there is no event that fires when local storage is changed by other code in the same window (the storage event only fires when storage is changed in other windows).

There are dodgy solutions like these, but really just make sure that your deletion code calls refreshData as part of its logic. Or actually, you don’t even need refreshData, you could maintain the array locally in the component and just echo it to local storage:

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>
    );
};

If you also want to listen for changes in other windows (users do that):

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>
    );
};