user3574939
user3574939

Reputation: 829

persist state after page refresh in React using local storage

What I would like to happen is when displayBtn() is clicked for the items in localStorage to display.

In useEffect() there is localStorage.setItem("localValue", JSON.stringify(myLeads)) MyLeads is an array which holds leads const const [myLeads, setMyLeads] = useState([]); myLeads state is changed when the saveBtn() is clicked setMyLeads((prev) => [...prev, leadValue.inputVal]);

In DevTools > Applications, localStorage is being updated but when the page is refreshed localStorage is empty []. How do you make localStorage persist state after refresh? I came across this article and have applied the logic but it hasn't solved the issue. I know it is something I have done incorrectly.

import List from './components/List'
import { SaveBtn } from './components/Buttons';

function App() {
  const [myLeads, setMyLeads] = useState([]);
  const [leadValue, setLeadValue] = useState({
    inputVal: "",
  });

  const [display, setDisplay] = useState(false);

  const handleChange = (event) => {
    const { name, value } = event.target;
    setLeadValue((prev) => {
      return {
        ...prev,
        [name]: value,
      };
    });
  };

  const localStoredValue = JSON.parse(localStorage.getItem("localValue")) ;

  const [localItems] = useState(localStoredValue || []);

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

  const saveBtn = () => {
    setMyLeads((prev) => [...prev, leadValue.inputVal]);
    // setLocalItems((prevItems) => [...prevItems, leadValue.inputVal]);

    setDisplay(false);
  };

  const displayBtn = () => {
    setDisplay(true);    
  };


  const displayLocalItems = localItems.map((item) => {
    return <List key={item} val={item} />;
  });

  return (
    <main>
      <input
        name="inputVal"
        value={leadValue.inputVal}
        type="text"
        onChange={handleChange}
        required
      />

      <SaveBtn saveBtn={saveBtn} />

      <button onClick={displayBtn}>Display Leads</button>

      {display && <ul>{displayLocalItems}</ul>}
    </main>
  );
}

export default App;```

Upvotes: 1

Views: 3437

Answers (1)

millhouse
millhouse

Reputation: 10007

You've fallen into a classic React Hooks trap - because using useState() is so easy, you're actually overusing it.

If localStorage is your storage mechanism, then you don't need useState() for that AT ALL. You'll end up having a fight at some point between your two sources about what is "the right state".

All you need for your use-case is something to hold the text that feeds your controlled input component (I've called it leadText), and something to hold your display boolean:

  const [leadText, setLeadText] = useState('')
  const [display, setDisplay] = useState(false)
  const localStoredValues = JSON.parse(window.localStorage.getItem('localValue') || '[]')

  const handleChange = (event) => {
    const { name, value } = event.target
    setLeadText(value)
  }

  const saveBtn = () => {
    const updatedArray = [...localStoredValues, leadText]
    localStorage.setItem('localValue', JSON.stringify(updatedArray))
    setDisplay(false)
  }

  const displayBtn = () => {
    setDisplay(true)
  }

  const displayLocalItems = localStoredValues.map((item) => {
    return <li key={item}>{item}</li>
  })

  return (
    <main>
      <input name="inputVal" value={leadText} type="text" onChange={handleChange} required />

      <button onClick={saveBtn}> Save </button>

      <button onClick={displayBtn}>Display Leads</button>

      {display && <ul>{displayLocalItems}</ul>}
    </main>
  )

Upvotes: 4

Related Questions