Search code examples
reactjsformslocal-storagecrudreact-context

How can I save the values in the context to local storage?


When I add or remove a contact from contact list in the context, I want it to be saved in memory (local storage). The code I wrote for local storage gives an error. How can I do it right? "State.contacts is iterable" error occurs. As soon as I take the code I wrote for Local, everything is fine. Where is the problem?

import React,{ createContext, useReducer } from "react";
import AppReducer from "./AppReducer";

const initialState = {
  contacts: [],
};

export const GlobalContext = createContext(initialState);

export const ContextProvider = ({children}) => {


  const [state, dispatch] = useReducer(AppReducer, initialState)

  // const [state, dispatch] = useReducer(AppReducer, initialState, () => {
  //   const localData  = localStorage.getItem("contacts");
  //   return localData ? JSON.parse(localData) : []
  // })
    

  // useEffect(() => {
  //   localStorage.setItem("contacts", JSON.stringify(state));
  // }, [state])

  const ADD_CONTACT = (contacts) => {
    dispatch({
      type: "ADD_CONTACT",
      payload: contacts,
    });
  };

  const REMOVE_CONTACT = (id) => {
    dispatch({
      type: "REMOVE_CONTACT",
      payload: id,
    });
  };

  const UPDATE_CONTACT = (contacts) => {
    dispatch({
      type: "UPDATE_CONTACT",
      payload: contacts,
    });
  };

  return (
    <GlobalContext.Provider
      value={{
        contacts: state.contacts,
        ADD_CONTACT,
        REMOVE_CONTACT,
        UPDATE_CONTACT,
      }}
    >
      {children}
    </GlobalContext.Provider>
  );
};

const AppReducer = (state, action) =>{
    switch(action.type){
        case "ADD_CONTACT" :{
            return{
                ...state,
                contacts : [...state.contacts,action.payload]
            }
        }
            
        case "REMOVE_CONTACT":{
            return{
                ...state,
                contacts:
                state.contacts.filter((contact)=>contact.id !== action.payload)
            }
        }
            
        case "UPDATE_CONTACT":{
            const updatedContact = action.payload;

            const updatedContacts = state.contacts.map(contact=>{
                if(contact.id === updatedContact.id){
                    return updatedContact
                }
                 return contact
            })
             return {
                ...state,
                contacts:updatedContacts
             }
        }
            
        default:{
            return state
        }
            
    }
}

export default AppReducer


Solution

  • I think you should rewrite both your reducer and your context provider to fixe your problem.

    //Your context provider
    
    // == React
    import React, { createContext, useReducer } from 'react'
    
    import { reducers } from './reducers'
    
    const ContactContext = createContext({})
    
    function ContextProvider({ initialState , children }) {
    
        const [ state, dispatch ] = useReducer(
    
            contactReducer,
            initialState,
            initialContacts => {
    
                const contacts = localStorage.getItem('contacts') 
                
    
               return { 
                   contacts: contacts
               }
            })
    
    
    
          return (
               <ContactContext.Provider value={{ state, dispatch }}>
                  {children}
               </ContactContext.Provider>
          )
        }
    

    And your reducer like so:

        export const APP_CONTEXT = {
            removeContact: 'REMOVE_CONTACT',
        }
    
        export function contactReducer(state, action) {
    
       try {
    
                switch (action.type) {
                case APP_CONTEXT.removeContact:
    
                localStorage.setItem('contacts', action.contacts)
    
                return {
                    ...state,
                    contacts: contacts
                }
    
                default: return {}
    
           }
    
         }
      }
    
    }