How to render every items from local storage in ToDo App

Viewed 191

How do I render all the items from local storage and display them as react component after every render? I am trying to make a toDo app where I'm trying to cache the data the user types into the checkboxes.

Whenever the user leaves the page and comes back, the data they typed into the checkboxes earlier is still shown. I've got this partially solved but the problem I am struggling here is not being able to display all the items from local storage as react components within the useEffect. What are good solutions to solve this problem?

**Tasks.JSX**
    import React, {useState, useEffect} from "react";
    import {Delete, Refresh, Add} from "../components/Actions";
    import {Header} from "../components/Header";
    import {PriorityLists} from "../components/PriorityLists";
    import {v4 as uuidv4} from 'uuid';
    import { serialize, deserialize } from "react-serialize";
    
    

    function Task() {
       
    
      
        const [toDo, setToDo] = useState([])
        const [idsToRefresh, setIdsToRefresh] = useState([]);
        const [filter_items, setFilterItems] = useState(false);
        const [ids, setIds] = useState([]);
       
        const [text, setTextValue] = useState({key:null, txt:null});
    
        useEffect(()=> {
            for(let i=0; i < localStorage.length; i++) {
                let storedValue = localStorage.key(i);
                let value = JSON.parse(localStorage.getItem(storedValue));
                if(typeof value === "string") {
                    setToDo(toDo.concat({_Id: storedValue, item_value: <PriorityLists id = {storedValue} checked = {false} addIds = {addIds} local_storage ={ls} text_value = {value} />}));
                    console.log(`${value} Index ${i}`);
                }
                    
                
                
               
            }
        }, [])
    
    
    
        
        useEffect(() => {
            if(toDo[0] !== undefined) {
                setToDo(toDo.filter(item=> {
                    return !ids.includes(item._Id);
                }))
               
            }
    
    
        }, [filter_items]);
    
        function addIds(checked, id_to_be_deleted) {
            if(!checked) {
                setIds((item)=> [...item, id_to_be_deleted]);
            }
            else {
                setIds(ids.filter(item=> {
                    return item !== id_to_be_deleted;
                }))
            }
        }
        
        function ls(e) {
            localStorage.setItem(JSON.stringify(e.target.id), serialize(e.target.value));
            
        }
    
        
    
    
    
        function addToDos() {
            const id = uuidv4();
         
            setToDo(toDo.concat({_Id: id, item_value: <PriorityLists id = {id} checked = {false} addIds = {addIds} local_storage ={ls} text_value = {null} />}));
            setIdsToRefresh(idsToRefresh.concat(id));
       
        }
    
      
        
    
        function refresh() {
            setToDo(toDo.filter(item=> {
                return !idsToRefresh.includes(item._Id);
            }))
        }
    
    
    
        return (
        <div className = "main-content">
            <div className = "container-fluid">
                <div className = "row underline">
                    <div className= "col">
                        <div className = "row">
                        <div className = "col-3 pt-2">
                            <Refresh _refresh = {refresh} />
                        </div>
    
                        <div className = "col-6 text-center">
                            <Header header ={"Tasks"}/>
                        </div>
    
                        <div className = "col-3 pt-2">
                            <button  className = "float-right">
                                <Delete setFilterItems = {setFilterItems} filter = {filter_items} />
                            </button>
                        </div>
    
                        </div>
                    </div>
                </div>
    
                <div className = "row">
                    <div className = "col">
              
                       {
                       
                       
                       toDo.map(item=> {
                           return (<div key = {item._Id}> 
                           <ul>
                               <li>{item.item_value}</li>
                             
                           </ul>
                           </div>)
                       })}
                        
                       
     
                        
                    </div>
                </div>
    
    
                <div className = "row">
                    
                    <div className = "col pr-4">
            
                        <Add addToDos = {addToDos} />
                    
                    </div>
                </div>
    
    
    
            
            </div>
        </div>
        
        )
    }
    
    export default Task;

PriorityLists.jsx

import React, {useState, useEffect} from "react";
import { Priority } from "./Actions";




function PriorityLists(props) {
    const [isChecked, setChecked] = useState(false);
    const [changeInput, setChangeInput] = useState(props.text_value);



    function check(e) {
        const id_to_be_deleted = e.target.attributes.getNamedItem("unique_Key").value;
       
        if(!isChecked) {
            setChecked(true);
            props.addIds(isChecked, id_to_be_deleted);

        }

        else {
            setChecked(false);
            props.addIds(isChecked, id_to_be_deleted)
        }
    }

    function setChange(e) {
        setChangeInput(e.target.value);
        props.local_storage(e);
    }

    

    return ( 
    <form key = {props.id}  >
       <div className = "input-group mb-3">
            <div className="input-group-prepend">
                <div className="input-group-text">
                <input unique_Key = {props.id}   onInput = {e=>   e.target.checked = isChecked} onClick={e=> check(e) } checked = {props.checked} id = "check-item" type="checkbox" aria-label="Checkbox for following text input"/>
                </div>
            </div>
            <textarea id = {props.id} onChange = {e=>setChange(e)} value = {changeInput}   class="form-control"  rows="1" name = {props.name}></textarea>
            <Priority id = {props.id} text_value = {changeInput} />
         </div>
   </form>
    )
}

export {PriorityLists};
2 Answers

Maybe you need to add a listener like this:

  useEffect(() => {
    function watchStorage() {
       for(let i=0; i < localStorage.length; i++) {
          let storedValue = localStorage.key(i);
             let value = JSON.parse(localStorage.getItem(storedValue));
               if(typeof value === "string") {
                    setToDo(toDo.concat({_Id: storedValue, item_value: <PriorityLists id = {storedValue} checked = {false} addIds = {addIds} local_storage ={ls} text_value = {value} />}));
                    console.log(`${value} Index ${i}`);
                }
                    
      }

    window.addEventListener("storage", watchStorage);
    return () => {
      window.removeEventListener("storage",watchStorage);
    };
  }, []);


Reference to this answer

One way is to add an event listener to the storage event which will trigger a function call every time.

Vo has mentioned a way to work with the storage event.

I would personally prefer if you loaded the data from storage on the inital mount, and stored it in a state inside a context shared across your components.

This way you can update the state directly and as a side effect, you can update the localStorage as well.

This in my opinion is a much cleaner approach.

  • create saveData loadData in a utils function for localStorage
  • loadData using key, and store as initState in AppContext
  • share state across views.
  • update the state
  • add a useEffect with state, and save data whenever state is updated

I hope this solves your problem

Related