How to make counter buttons persistent after page reload

Viewed 438

I am trying to implement counter buttons that should be persistent after page reload. Currently my buttons all increment individually but at page, reload only the first/top one is affected. If I click other counter buttons, when I reload page, the first one shows the count of the last button I clicked before I reloaded page. I have buttons for every user I am mapping in the data, but it sees only one button it seems. I need the counter component to dynamically add counter buttons for every user.

I read that one issue with storing state in localStorage at the component level with multiple instances of the same component can lead to unexpected behavior. So I am guessing one solution is using Redux to store the state but I have had no luck implementing it so far. How should I go about doing this?

Here is my button Component:

import React, { useState } from "react";

function Counter() {
  const [count, setCount] = useState(0);

  React.useEffect(() => {
    const parsedCount = Number(localStorage.getItem("count") || 0)
    setCount(parsedCount)
  }, [])

  React.useEffect(() => {
    localStorage.setItem("count", count)
  }, [count])

  const handleIncrement = () => {
    setCount(prevCount => prevCount + 1);
  };

  return (
    <div>
      <h5>Count is {count}</h5>
      <button onClick={handleIncrement}>+</button>
    </div>
  );
}

And here is my App.js where I am calling the counter button:

import React from 'react';
import Counter from "./components/Counter";
import './App.css';
import { useAsync } from 'react-async';

function App() {

const loadUsers = async () =>
  await fetch("")
    .then(res => (res.ok ? res : Promise.reject(res)))
    .then(res => res.json())

function App() {
  const { data, isLoading } = useAsync({ promiseFn: loadUsers })
  if (isLoading) return "Loading..."
  if (data)

  return (
    <div className="container">
      <div>
      </div>
      {data.map(user=> (
        <div key={user.username} className="row">
          <div className="col-md-12">
            <h2>{user.name}</h2>
            <h3>{user.title}</h3>
            <Counter/>
          </div>
        </div>
      ))}
    </div>
  );
}

export default App;
2 Answers

Problem

You are getting the last clicked button value because you are using the same local storage key for every counter.

Solution

Add a property to the Counter component to identify every counter:

function Counter({id}) {
  const [count, setCount] = useState(0);
  const KEY = `count-${id}`;

  React.useEffect(() => {
    const parsedCount = Number(localStorage.getItem(KEY) || 0)
    ...
  }, [])

  React.useEffect(() => {
    localStorage.setItem(KEY, count);
  }, [count]);
...
}

Use:

function App() {

  return (
    <div className="container">
      <div>
         <Counter id="counter-1"/>
      </div>
    </div>
  );
}

Working example

Your state will be refreshed whenever you refresh the browser. But you can set state within your browser.

localStorage.setItem('abc',1)

Above is an example of how we can set the state of abc to 1 in the local storage.

After refreshing the page we can do:

abc = localStorage.getItem('abc')

And so abc will equal "1" (note that it will return a string).

As far as your react application goes, when the component is first mounted/initialized, you can read localStorage and update your state accordingly.

Related