Search code examples
reactjsreact-hookslocal-storageweb-frontend

Can't persist data on localStorage after page refresh using React useEffect hook


I'm following a tutorial building a frontend project for recipes and I can't replicate the behavior of persisting the data (that is, if I delete one default recipe and add a new recipe, it should reflect the same after page refresh rather than getting populated by default recipes again). I want to make changes to the page and want it to stay the same after the page refresh.

I'm using React useEffect hook and localStorage for this purpose.

The required code and screenshots are posted below. Please add any of your inputs. All suggestions are welcome. Thanks.

CodeSandbox link: https://codesandbox.io/s/github/ft76/NewNew CSS styles do not reflect here, but feature additions by React like AddRecipe button works fine.

There are other module files for handling recipes etc and I'm going to exclude them because they don't have anything to do with the issue on hand. The code is the following:

import React, {useState, useEffect} from 'react';
import RecipeList from './RecipeList';
import '../css/app.css';
import { v4 as uuidv4 } from 'uuid';

export const RecipeContext = React.createContext()
const LOCAL_STORAGE_KEY = 'cookingWithReact.recipes';

function App() {
  const [recipes, setRecipes] = useState(sampleRecipes)

  useEffect(() => {
    const recipeJSON = localStorage.getItem(LOCAL_STORAGE_KEY);
    if (recipeJSON !== null) {
      setRecipes(JSON.parse(recipeJSON))
    }
  }, [])

  useEffect(() => {
    localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(recipes))
  }, [recipes])

  const recipeContextValue = {
    handleRecipeAdd,
    handleRecipeDelete
  }

  function handleRecipeAdd() {
    const newRecipe = {
      id: uuidv4(),
      name: 'New',
      servings: 1,
      cookTime: '1:00',
      instructions: 'Instructions',
      ingredients: [
        {id: uuidv4(), name: 'Name', amount: '1 Tbs'}
      ]
    }

    setRecipes([...recipes, newRecipe])
  }

  function handleRecipeDelete(id) {
    setRecipes(recipes.filter(recipe => recipe.id !== id))
  }

  return (
    <RecipeContext.Provider value={recipeContextValue}>
      <RecipeList recipes={recipes} />
    </RecipeContext.Provider>
  )
}

const sampleRecipes = [
  {
    id: 1,
    name: 'Plain Chicken',
    servings: 3,
    cookTime: '1:45',
    instructions: "1. Put salt on chicken.\n2. Put chicken in oven.\n3. Eat the chicken.",
    ingredients : [
      {
        id: 1,
        name: 'Chicken',
        amount: '2 pounds'
      },
      {
        id: 2,
        name: 'Salt',
        amount: '1 Tbs'
      }
    ]
  },
  {
    id: 2,
    name: 'Plain Pork',
    servings: 5,
    cookTime: '0:45',
    instructions: "1. Put paprika on pork.\n2. Put pork in oven.\n3. Eat the pork.",
    ingredients : [
      {
        id: 1,
        name: 'Pork',
        amount: '3 pounds'
      },
      {
        id: 2,
        name: 'Paprika',
        amount: '2 Tbs'
      }
    ]
  }
]

export default App;

The required screencaps are as follows:

Default screen: enter image description here

Screen after deleting one default recipe and adding a new one: enter image description here

Screen after refreshing the page: enter image description here


Solution

  • The mistake in your code is that you were retrieving data from a const array of objects.

    I have changed sampleRecipes with a new array sampleRecipes2 and it will check if the local storage is empty or not, that part is this.

    You can initialize sampleRecipes2 with any array.

    let sampleRecipes2 = [];
      let info = localStorage.getItem(LOCAL_STORAGE_KEY);
      if (info === null) {
        sampleRecipes2 = sampleRecipes;
      } else {
        sampleRecipes2 = JSON.parse(info);
      }
    

    Overall this should be your App.js,

    import React, { useState, useEffect } from "react";
    import RecipeList from "./RecipeList";
    import "../css/app.css";
    import { v4 as uuidv4 } from "uuid";
    
    export const RecipeContext = React.createContext();
    const LOCAL_STORAGE_KEY = "cookingWithReact.recipes";
    function App() {
      let sampleRecipes2 = [];
      let info = localStorage.getItem(LOCAL_STORAGE_KEY);
      if (info === null) {
        sampleRecipes2 = sampleRecipes;
      } else {
        sampleRecipes2 = JSON.parse(info);
      }
      const [recipes, setRecipes] = useState(sampleRecipes2);
      useEffect(() => {
        localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(recipes));
      }, [recipes]);
    
      const recipeContextValue = {
        handleRecipeAdd,
        handleRecipeDelete
      };
    
      function handleRecipeAdd() {
        const newRecipe = {
          id: uuidv4(),
          name: "New",
          servings: 1,
          cookTime: "1:00",
          instructions: "Instructions",
          ingredients: [{ id: uuidv4(), name: "Name", amount: "1 Tbs" }]
        };
    
        setRecipes([...recipes, newRecipe]);
      }
    
      function handleRecipeDelete(id) {
        setRecipes(recipes.filter((recipe) => recipe.id !== id));
      }
    
      return (
        <RecipeContext.Provider value={recipeContextValue}>
          <RecipeList recipes={recipes} />
        </RecipeContext.Provider>
      );
    }
    
    const sampleRecipes = [
      {
        id: 1,
        name: "Plain Chicken",
        servings: 3,
        cookTime: "1:45",
        instructions:
          "1. Put salt on chicken.\n2. Put chicken in oven.\n3. Eat the chicken.",
        ingredients: [
          {
            id: 1,
            name: "Chicken",
            amount: "2 pounds"
          },
          {
            id: 2,
            name: "Salt",
            amount: "1 Tbs"
          }
        ]
      },
      {
        id: 2,
        name: "Plain Pork",
        servings: 5,
        cookTime: "0:45",
        instructions:
          "1. Put paprika on pork.\n2. Put pork in oven.\n3. Eat the pork.",
        ingredients: [
          {
            id: 1,
            name: "Pork",
            amount: "3 pounds"
          },
          {
            id: 2,
            name: "Paprika",
            amount: "2 Tbs"
          }
        ]
      }
    ];
    
    export default App;