Home > front end >  LlocalStorage doesn't display the data after submit and state doesn't persist on refresh
LlocalStorage doesn't display the data after submit and state doesn't persist on refresh

Time:08-02

I'm writing a simple todo app and trying to save the data in local-storage. I've watched several tutorials considering using localStorage in React and did as they said step by step, and the code seems to be just fine. What seems to be the problem, is the local-storage itself. It should update automatically and display data when I add a todo item using localStorage.setItem(), but it doesn't. Only when I refresh it (the little refresh button just over the field with key-value pairs in the console in Application -> Local Storage) I can see the key-value pair displayed, so it looks like it has worked. However, even though it seems to keep the data after refreshing it manually, when I refresh the whole page, the data disappear. Below I included most of my code, perhaps I'm missing something and someone will notice the bug. Could someone help?

const [todos, setTodos] = useState([]);

  const addTodo = (e) => {
    e.preventDefault();
    if (inputValue.trim() === "") return;
    setTodos([
      ...todos,
      {
        text: inputValue,
        id: uuidv4(),
      },
    ]);

    setInputValue("");
  };

  useEffect(() => {
    const localData = localStorage.getItem("TODO_APP");
    if (localData !== null) setTodos(JSON.parse(localData));
  }, []);

  useEffect(() => {
    localStorage.setItem("TODO_APP", JSON.stringify(todos));
  }, [todos]);

CodePudding user response:

Looks like useEffect conflict. Move the initialization into the useState call.

const [todos, setTodos] = useState(() => {
  const localData = localStorage.getItem("TODO_APP");
  if (localData !== null) return JSON.parse(localData);
  return [];
});

const addTodo = (e) => {
  e.preventDefault();
  if (inputValue.trim() === "") return;
  setTodos([
    ...todos,
    {
      text: inputValue,
      id: uuidv4(),
    },
  ]);

  setInputValue("");
};

useEffect(() => {
  localStorage.setItem("TODO_APP", JSON.stringify(todos));
}, [todos]);

CodePudding user response:

I will add another slightly modified version. Should work:

const [todos, setTodos] = useState(JSON.parse(localStorage.getItem('TODO_APP')) || []);

const addTodo = (e) => {
    e.preventDefault();
    
    if (inputValue.trim() !== '') {
        setTodos([
            ...todos,
            {
                text: inputValue,
                id: uuidv4()
            }
        ]);
    }

    setInputValue('');
};

useEffect(() => {
    localStorage.setItem('TODO_APP', JSON.stringify(todos));
}, [todos]);

CodePudding user response:

Other solutions might work for you, but if you are using React 18, your code is perfectly fine, the issue was the useEffects called twice on refresh which was resetting localStorage. This is a kind of known issue.

You just need to disable StrictMode in the src/index.js file

index.js

import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<App />);

reportWebVitals();

I tried to complete the missing part of the component and the final component looks like this:

import { useState, useEffect } from "react";

const Todo = () => {
  const [todos, setTodos] = useState([]);
  const [inputValue, setInputValue] = useState("");

  const onChangeHandler = (e) => {
    setInputValue(e.target.value);
  };

  const addTodo = (e) => {
    e.preventDefault();
    if (inputValue.trim() === "") return;
    setTodos([...todos, { text: inputValue }]);

    setInputValue("");
  };

  useEffect(() => {
    console.log("1st");
    const localData = localStorage.getItem("TODO_APP");
    console.log(localData);
    if (localData.length !== 0) setTodos(JSON.parse(localData));
  }, []);

  useEffect(() => {
    console.log("2nd");
    localStorage.setItem("TODO_APP", JSON.stringify(todos));
  }, [todos]);

  return (
    <form onSubmit={addTodo}>
      <input type="text" value={inputValue} onChange={onChangeHandler}></input>
      <button type="submit">ADD TODO</button>
    </form>
  );
};

export default Todo;

I added console.log() in the code sso you can observe how these useEffects are called on refresh

  • Related