Benison
Benison

Reputation: 167

localStorage getting updated with initial state value on refresh

I am trying to use localStorage to persist the component's state on refresh. The component works fine on initial render of the page, but the component fails with the message saying: TypeError: items.map is not a function

TestLocalStorage
src/TestLocalStorage.jsx:54

Here's the code:

import React, { useEffect, useState } from 'react';

function TestLocalStorage() {
    const [items, setItems] = useState([]);

    useEffect(() => {
        const storedData = localStorage.getItem("items");
        if(!storedData) {
            Axios.get('/url').then(response => {
                const serviceResponse = response.data.hits.map(obj=> ({ ...obj, voteCount: 0 
           }));
                localStorage.setItem("items", JSON.stringify(serviceResponse));
                setItems(serviceResponse);
            });
        } else {
            setItems(storedData);
        }
    }, []);

    useEffect(() => {
        localStorage.setItem("items", JSON.stringify(items));
    })

    function increment(id) {
        const elementsIndex = items.findIndex(element => element.objectID === id )
        items[elementsIndex].voteCount++;
        setItems([...items]);
    }

    return (
        <table cellPadding="0" cellSpacing="0" width="100%" background-color="#f6f6ef">
         <tr>
            <td>
               <table border="0" cellPadding="0" cellSpacing="0" width="100%">
                    <tr bgcolor="#ff6600" style={{ color:"white", fontWeight: "bold" }}>
                        <td> Comments </td>
                        <td> Vote Count</td>
                        <td> vote</td>
                    </tr>
                    {
                        items.map((item) => {
                            return (
                            <tr className="table-rows" key={item.objectID}>
                                <td>{ item.comments }</td>
                                <td>{ item.voteCount }</td>
                                <td>
                                    <div 
                                        style={{ 
                                            width: 0, 
                                            height: 0, 
                                            borderTop: 0, 
                                            borderLeft: "5px solid transparent", 
                                            borderRight: "5px solid transparent", 
                                            borderBottom: "10px solid grey" 
                                        }}
                                        onClick={() => increment(item.objectID)} 
                                    />
                                </td>
                                <td>{ item.title }</td>
                            </tr>)
                        })
                    }
               </table>
            </td>
         </tr>
      </table>
    );
}
export default TestLocalStorage;

But I noticed that on second render the localStorage has initial value [] set instead of the newly updated one. Not sure where I am going wrong. Please help

Upvotes: 0

Views: 1191

Answers (2)

Dinesh Nadimpalli
Dinesh Nadimpalli

Reputation: 1491

Do the parsing on the string which you are extracting from local storage by using JSON.parse()

function TestLocalStorage() {

        const [items, setItems] = useState([]);
    
        useEffect(() => {
            const storedData = localStorage.getItem("items");
            if(!storedData) {
                Axios.get('/url').then(response => {
                    const serviceResponse = response.data.hits.map(obj=> ({ ...obj, voteCount: 0 
               }));
                    localStorage.setItem("items", JSON.stringify(serviceResponse));
                    setItems(serviceResponse);
                });
            } else {
                setItems(storedData);
            }
        }, []);

        ...............
        ...............
        ...............

}

Modify your function like below. Also, do a check for items value before doing map operation on the array to prevent uncaught exceptions

function TestLocalStorage() {

        const [items, setItems] = useState([]);
    
        useEffect(() => {
            const storedData = JSON.parse(localStorage.getItem("items"));
            if(!storedData) {
                Axios.get('/url').then(response => {
                    const serviceResponse = response.data.hits.map(obj=> ({ ...obj, voteCount: 0 
               }));
                    localStorage.setItem("items", JSON.stringify(serviceResponse));
                    setItems(serviceResponse);
                });
            } else {
                setItems(storedData);
            }
        }, []);

        ...............
        ...............
        ...............
        return (
           .....
           .....
           items && items.map((item) => {
           .....
           .....
        )
}

Upvotes: 2

Ardeshir Izadi
Ardeshir Izadi

Reputation: 1073

storedData will be string, you need to JSON.parse(storedData) to be able to use that and do map on it

Upvotes: 2

Related Questions