MGmgmgmg
MGmgmgmg

Reputation: 81

Save state to localstorage

I'm new to react, I have a search bar, when I enter something, it automatically updates the results, but I want to be able to navigate away from the page and when I come back, I want it to be persisted. The search should be retained until manually cleared by the user.

This is my code so far:

    import { useEffect, useRef, useState } from "react";
import { useOnClickOutside } from "../../hooks";
import useDebounce from "../../hooks/useDebounce";
import { ISearchProps } from "./search.props";
import { SearchWrapper } from "./Search.style";

const Search: React.FC<ISearchProps> = ({onChange, placeholder = "Search by..."}) => {
    
    const [value, setValue] = useState<string>("");
    const debouncedValue = useDebounce(value, 500);
    const [expandInput, setExpandInput] = useState(false);
    const ref = useRef(null);
    const [input, setInput] = useState<HTMLInputElement | null>();

    useEffect(()=>{
        onChange(debouncedValue);
        window.localStorage.setItem('SAVE_SEARCH', JSON.stringify(value));
    }, [debouncedValue]);

    useEffect(()=>{
        const data = window.localStorage.getItem('SAVE_SEARCH');
        if (data !== null) 
        setValue(JSON.parse(data))
    }, [setValue]);

    useEffect(()=>{
        expandInput && input?.focus();
    }, [expandInput]);

    const searchHandler = (e: React.ChangeEvent<HTMLInputElement>) => {
       setValue(e.target.value);
    }
    
    useOnClickOutside(ref, () => value === "" && setExpandInput(false));
    

    return(
        <SearchWrapper expandInput={expandInput} ref={ref}>
            <input ref={inputEl => setInput(inputEl)} type="text" onChange={(e) => searchHandler(e)} value={value} placeholder={placeholder} />
            { (expandInput && (value.length > 0)) && <i className="ico-Exit" onClick={()=> setValue("")} ></i>}
            <button className="search-button" onClick={() => setExpandInput(true)}>
                <i className="ico-Search"></i>
            </button>
        </SearchWrapper>
    )
}

export default Search; 

I can see this in dev tools > Storage, it's updating as I type: enter image description here

Not sure what I'm missing, please help!

Upvotes: 3

Views: 10078

Answers (3)

Vijay Dhanvai
Vijay Dhanvai

Reputation: 1116

First, set localStorage with the same value which you are storing in to State.

  const saveCartData = (cartData) => {
    setCartItemsList(cartData);
    localStorage.setItem("cartItems", JSON.stringify(cartData));
  };

Second while initializing your state get the localStorage value

const cartInitialValue = () => {
  return JSON.parse(localStorage.getItem("cartItems")) || [];
};

Apply local storage value to initial value of state.

  const [cartItemsList, setCartItemsList] = useState(cartInitialValue);

Upvotes: 1

Jan
Jan

Reputation: 99

Write yourself a custom react hook:

import { useEffect, useState } from "react";

export default function usePersistantState<T>(
   key: string,
   initialValue: T
): [T, (value: T) => void] {
   const [state, setInternalState] = useState<T>(initialValue);

   useEffect(() => {
       const value = localStorage.getItem(key);

       if (!value) return;

       setInternalState(JSON.parse(value));
   }, [key]);

   const setState = (value: T) => {
       localStorage.setItem(key, JSON.stringify(value));
       setInternalState(value);
   };

   return [state, setState];
}

Upvotes: 8

Jo&#227;o Santos
Jo&#227;o Santos

Reputation: 101

You can initialize your state by getting the value of local storage. useState hook accept a function as a parameter.

  const [value, setValue] = useState(window.localStorage.getItem('SAVE_SEARCH'))
   

Now, when your component would render, it will get the value that is on local storage and assign to your state.

But, it would be easier if you would use React Context to manage global values.

https://reactjs.org/docs/context.html#when-to-use-context

Upvotes: 2

Related Questions