React useState not updating the variable

Problem

  • There is no syntax errors.
  • Function parseLocalStorage returns a object
  • White using setStorage(parseLocalStorage()) at useState
    • Storage does’nt get modified
    • setStorage used in other functions (like <Element setStorage={setStorage} / > ) stops working
  • I have searched all other occurrences of setStorage but they aren’t the cause as they occur onEvents(like when the message is sent, a new chat etc.)

Code

Highly simplified as Orginal is large

const Main = () =>{ 
const [hasParsed , setHasParsed] = useState(false);
const [storage, setStorage] = useState({ 
    rootId: "1",                           
    items: {
      "1": {
        id: "1",
        children: ["1-1"],
        hasChildren: true,
        isExpanded: true,
        isChildrenLoading: false,
        data: {
          title: "root"
        }
      },
      "1-1" : {
        id: "1-1",
        children: ["1-1-1"],
        hasChildren: true,
        isExpanded: false,
        isChildrenLoading: false,
        data: {
          title: "folder-new Folder"
        }
      },
      "1-1-1" : {
        id: "1-1-1",
        children: [],
        hasChildren: false,
        isExpanded: false,
        isChildrenLoading: false,
        data: {
          title: "chat-New Chat",
          messages : []
        }
      }
    }
  });

 const parseLocalStorage = () =>{
         // parses localStorage perfectly and returns it
       return parsedLocalStorage;
} 

useState(() =>{
   if(!hasParsed)
{
      setStorage(parseLocalStorage());
     setHasParsed(false);
     // This part causes issues 
    // Storage does'nt get modified
    //  - setStorage used in other functions 
    // (like <Element setStorage={setStorage} / >  ) stops   working

}
}

render() {
 return(
    <div>
// This element probably did'nt cause the issue as it only modifies the storage at button click.
        <Element setStorage={setStorage} />
    </div>
   )
}

}

export default Main;

Situation:

  • I am making a ChatGPT client using React.
  • I have a feature where the data of of the chat messages is copied to the localStorage when the a message is sent or a chat or folder is created.
  • The localStorage has to be read and copied to the storage variable when the app starts.
  • I have a function called parseLocalStorage which does this successfully but the setStorage does’nt seem to work…
  • I used the @atlaskit/tree package to generate the folder structure

why are you using useState here in kind of useeffect fashion? not assigning it to anything? It looks like you need useEffect or some other hook here?

I meant useEffect,made a typo. Sorry.

Why your useEffect does not have dependency array? Without it, it would be infinite re-rendering kind of situation potentially?

Meaning it will run on every re-render, and since it has state setters in the guts - it will trigger re-rendering by itself…

I see

this condition, but your code rn looks like hasParsed always falsy, so stuff inside effect will run always

I tried to solve this problem earlier by added a dependancy array hoping it would solve it like so


useEffect(() => {
    if(!hasParsed)
    {
      let parsedStore = parseLocalStorage();
      setStorage(parsedStore); 
      setHasParsed(true);
 
     console.log("parsedStorage" , parsedStore , "storage" , storage);
    }
      
  },[hasParsed]);

This seems to run once as intended, still. This is the result of the console.log() earlier…
As you can see parsedStore !== storage even though I used setStorage(parsedStore)

there is some tricky part about console-logging like this:
from the docs:

The set function only updates the state variable for the next render. If you read the state variable after calling the set function, you will still get the old value that was on the screen before your call.

refer to this: https://react.dev/reference/react/useState

to track state for testing purposes I would suggest to use react dev tools, which is browser extension

But , the <Element / > tag doesn’t re-render based on the new storage. The storage variable changes after sometime, as I noticed, but the tags to which it is passed as props doesn’t get re-rendered.

Give me a moment I will post a video demo…

but you are passing setStorage, which is the setter function…

you are not passing state variable it seems like
setStorage is a function, you can probably check it out by console.log in any place of your component, and it will always be the sam

I passed in way more variables to that <Element / > I didnt want to clutter the code and didnt give in all the variables…

hm, than I don’t know rn
if you are passing storage variable, among others, to the child, it should be all fine there.
Unless there are some possible issues in child components logic

Update. Just recalled some nuance

You mentioned in original post:

I did not see the code of Element component, but

your Main component modifies the storage state

your Element component modifies the storage state

I’ve learned that this is not very good situation and could lead to unexpected behaviour, since the same state ‘has 2 sources of truth’.
Trying to find some reference in docs right now, in case I am confusing some stuff

Check out these videos. I am really sorry to disturb you, but thank you for your help, i have been trying to solve this for hours now. Hopefully, I can solve it with your help…

As you can see in video where useEffect is used, the storage updates as intended but the component is not getting updated.

That’s not the case when useEffect is not used…

I can’t understand why a useEffect that runs once in the code would prevent the component from re-rendering

you are using effect like here?

or did you changed smthing from this version? I mean in videos

And don’t worry about disturbing me, I am learning a bunch from troubleshooting things like this one

1 Like

Yep. That’s how i am doing it.

lets try to crack first render

hasParsed is false here

thus

useEffect will run, cause it is first render

all stuff will also run inside effect, cause above condition is true

however

setters inside effect - will trigger re-render
hasParsed will set to true

Now lets crack the render after first:

hasParsed is true

useEffect still will run, cause hasParsed changed - so reactive dependency changed

but

any stuff inside if check will not run, so - no further state updates, including hasParsed

so it will stay true forever, and any further updating will knida be not possible

Please correct me if you see any mistakes in this explanation.

1 Like

Yep. No mistakes in the explanation, but the <Element storage={storage} / > doesn’t re-render even if the storage is changed after that…

so I would try to fix conflict logic here: hasParsed changing and the way how if check established - looks like it is causes updating issues

that’s a mistery for now, need to think more about it

But how would that cause updating issues? Should i just abstain from using ‘has 2 sources of truth’. and remove the setStorage and maybe use a setter function for the <Element / >?

Would you like to hop on live share on VS Code?

is that vs code extension? I’ve never used it yet. We’ll spend too much time on me figuring out setup