ShaneOG97
ShaneOG97

Reputation: 530

LocalStorage not working properly ReactJS

I have an array set up in a table so that when I click on an icon, a different icon is re-rendered in that specific row. Now I have localstorage setup so that when I refresh the page, the state is maintained. The problem I am having though is if I click 4 icons, 4 different icons are obviously re-rendered but when I refresh the page, the last one I click is reverted to its original state so now I only have 3 of the re-rendered icons. How do I fix this?

import React from 'react';
import StarBorder from '@material-ui/icons/StarBorder';
import Star from '@material-ui/icons/Star';
import axios from 'axios';

class Test extends React.Component {
    constructor(props) {
        super(props);

        var newData = [];

        if (localStorage.getItem('new')) {
            newData =  JSON.parse(localStorage.getItem('new'));
        }

        this.state = {
          data: newData,
        }
    }

    componentDidMount() {

        if (!localStorage.getItem('new')) {
            axios.get('https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&order=market_cap_desc&per_page=100&page=1&sparkline=true')
            .then(res => {
                const data = res.data;
                this.setState({  data: data.map(x => ({...x, starIcon: true})) })
            })
        }
    }

    handleClick = (i) => {

        this.setState(prevState => ({
          data: prevState.data.map((x, key) => (key === i ? {...x, starIcon: !x. starIcon} : x))
        }));

          localStorage.setItem('new', JSON.stringify(this.state.data));
      }

    render() {
        return (
        <div>
            <table border="1">
                <thead>
                    <tr>
                    <td>Icon</td>
                    <td>Name</td>
                    <td>Price</td>
                    </tr>
                </thead>
                <tbody>
                {this.state.data.map((n, i) => {
                    return (
                        <tr>
                        <td> <span onClick={() => this.handleClick(i)}> {n.starIcon ? <StarBorder/> : <Star /> } </span> </td>
                        <td>{n.name}</td>
                        <td>{n.current_price}</td>
                        </tr>
                    );
                    })}
                </tbody>
            </table>
        </div>
        );
    }
}

export default Test;

Upvotes: 2

Views: 9184

Answers (2)

niks
niks

Reputation: 466

You will have to change handleClick function, Because before setState set its state, local storage setitem method getting called so. So you have to use callback function of setState method

Why is setState in reactjs Async instead of Sync?

handleClick = (i) => {
   this.setState(prevState => ({
     data: prevState.data.map((x, key) => (key === i ? {
       ...x,
       starIcon: !x.starIcon
     } : x))
   }), () => {
     localStorage.setItem('new', JSON.stringify(this.state.data));
   });
 }

Upvotes: 0

Clarity
Clarity

Reputation: 10873

setState is async, so you when you set items to local storage you may still capture previous state. To fix it, use setState's callback function to make sure the state is updated when you set the items:

  handleClick = (i) => {
   this.setState(prevState => ({
     data: prevState.data.map((x, key) => (key === i ? {
       ...x,
       starIcon: !x.starIcon
     } : x))
   }), () => {
     localStorage.setItem('new', JSON.stringify(this.state.data));
   });
 }

Upvotes: 4

Related Questions