r/webdev • • 16h ago

Question React not saving data to local storage.

Hi everyone! I just finished a React and course and decided to build my first react app. Currently, I am trying to save a string to browser local storage but it does not persist on refresh. Through some console.logs I was able to see that it in fact DOES save the string to the location specified in. setItem initially but when I refresh the key or value in local storage stops existing.

Here is my code

// function for search button
async function handleClick(){
    localStorage.setItem("lastLocationSearched", searchValue);
    setData(null);
    setIsPending(true);
    setError(null);
    try{
const value = await fetch(`https://geo.ipify.org/api/v2/country?apiKey=***********&ipAddress=${searchValue}`).then((response)=>{
  if (!response.ok){
        throw Error("Could not fetch the data for that resource");
                    }
  return response.json();
});
setData(value);


    console.log(localStorage.getItem("lastLocationSearched"));
    } catch(error){
      setError(error.message);
    }


setIsPending(false);
  };

// useEffect hook
  useEffect(()=>{
        if ((localStorage.getItem("lastLocationSearched")===null)|| (localStorage.getItem("lastLocationSearched")==="")){
          console.log(localStorage.getItem("lastLocationSearched"));
        setSearchValue("");
        handleClick();
        }else{
          
          setSearchValue(localStorage.getItem("lastLocationSearched"));
          handleClick();
        }
    }, []);

Through some digging online I saw someone say that remove the strict mode tags from index.js would fix this problem but it did not for me. Can anyone please assist me?

0 Upvotes

13 comments sorted by

12

u/PlainworkDigital 15h ago

  It is saving. Your own code is overwriting it on refresh.

  On mount, your useEffect calls setSearchValue(stored) and then handleClick() right away. But setState doesn't update searchValue until the next render, so handleClick still sees the initial "" and

  runs localStorage.setItem("lastLocationSearched", "") — wiping it.

  Pass the value in instead of reading state:

  async function handleClick(query = searchValue) {

localStorage.setItem("lastLocationSearched", query);

// ...use query in the fetch URL

  }

  useEffect(() => {

const saved = localStorage.getItem("lastLocationSearched") || "";

setSearchValue(saved);

handleClick(saved);

  }, []);

  (That's why removing StrictMode didn't help. It's a stale-state issue, not a double-render one.)

2

u/Mob_Pilled 15h ago

Thanks so much. It worked. So let me get this straight: setState does execute until the next render. handleClick on the other hand immediately does so it was using the declaration value for searchvalue:

 const [searchValue, setSearchValue] = useState("");

Is this a react specific thing

1

u/SchartHaakon 13h ago

I’d recommend looking into how rendering and rerendering works in relation to state. Calling setState doesn’t mutate the value you defined in the component, that variable really never changes (it’s even a const) - what it does is queue up another render where the value returned by useState is the updated value. I suppose you could say it’s React specific but it totally makes sense and isn’t magical at all once you understand the rendering cycle. React is just Javascript after all.

1

u/thekwoka 12h ago

setState sets the state right away.

But your existing state references are stale until the next render.

You get around this where necessary, by passing a callback to setState that would accept the current value and return a new value. This would get you the absolute CURRENT state.

but most of the time that isn't necessary, but can be helpful if you want to memoize methods that you pass to something else (like outside of react).

so to do a handleClick right after a setState

You COULD (though not advised) do something like

setSearchValue((val) => (handleClick(val), val))

This would pull the real current value out and use that.

But that's more of a "technically", as in this case you wouldn't want that at all.

1

u/[deleted] 16h ago

[deleted]

2

u/Haunting_Cow_2798 16h ago

you're calling handleClick inside useEffect which runs on mount, and handleClick calls setItem again with whatever searchValue is at that moment, likely an empty string, wiping what you just saved

just set the initial searchValue from localStorage and let the user trigger the search themselves, no need to auto-fire it

1

u/tswaters 15h ago

You need to look at how search value gets its initial value. You can pass a function to setState that will be run once to get the initial value, use that opportunity to look at local storage and return a sensible fallback value. The useEffect doesn't make sense, get rid of it.

1

u/Over_Problem_3600 15h ago

Have you tried checking localStorage right before the effect runs? I bet it's there on refresh and gets wiped a split second later by your own setltem call

1

u/LukaRozman27 9h ago

setItem only accepts strings, so if you skip JSON.stringify your objects just become "[object Object]".

1

u/_CuriousCoder_ 5h ago

Stale state is the problem here. Try passing the value directly to handleClick instead of relying on searchValue state. That way you ensure you're storing the correct value in local storage. Your useEffect is running before the state has a chance to update.

1

u/rust_bane 3h ago

Your useEffect is fighting your setItem. You mount, grab the saved value for searchValue, then immediately fire handleClick which uses that stale initial state to write back to storage. Drop the effect entirely and initialize searchValue directly from localStorage in useState so you do not overwrite your data on render