0

I'm trying to do a really basic todo app in React using local storage, and am using an array of objects for my data. This seems to work fine for loading information up, but no matter what I do I can't seem to update local storage.

I started backtracking and found that after my handleSave function, if I console log this.state.todoData I get the correct, updated values. If, immediately after that console log, I try logging JSON.stringify(this.state.todoData) it brings me back the old data in stringified form, rather than the updated data. This is even before I do anything with local storage.

I can't for the life of me understand why console logging this.state.todoData vs JSON.stringify(this.state.todoData) would bring me a different data set.

Here is my handleSave function:

handleSave (idNum, title, due, description) {
  this.setState(prevState => ({
    todoData: prevState.todoData.map(obj =>
      obj.id === idNum
        ? Object.assign(obj, {
            inEditMode: false,
            title: title,
            due: due,
            description: description
          })
        : obj
    )
  }))
  console.log(this.state.todoData)
  console.log(JSON.stringify(this.state.todoData))
}

Screenshot of the console: notice the title of the Todo at Index 0

halfer
  • 19,824
  • 17
  • 99
  • 186
AJM
  • 1
  • 1
  • The trick with React is that the state is not immediately updated – user0101 Oct 06 '20 at 10:47
  • [Why is setState in reactjs Async instead of Sync?](https://stackoverflow.com/questions/36085726/why-is-setstate-in-reactjs-async-instead-of-sync), [State and Lifecycle – React](https://reactjs.org/docs/state-and-lifecycle.html) – Andreas Oct 06 '20 at 10:54

3 Answers3

1

Since the state is not immediately updated you should instead update/log the values after it successfully completed, for instance in the callback of the setState function

this.setState(prevState => ({
  todoData: prevState.todoData.map(obj =>
    obj.id === idNum ?
    Object.assign(obj, {
      inEditMode: false,
      title: title,
      due: due,
      description: description
    }) :
    obj
  )
}), () => {
  console.log(this.state.todoData)
  console.log(JSON.stringify(this.state.todoData))
})
Krzysztof Krzeszewski
  • 5,912
  • 2
  • 17
  • 30
  • Just to add to this answer: The reason your logging is confusing is that your browser's log uses references. When objects that are logged are changed, they may sometimes show you the updated value instead of the state just as it was when logged – Simen Fjellstad Oct 06 '20 at 10:53
  • "Since the state is not immediately updated" I think this is what I suspected the situation was, but also the part I don't entirely understand. I was Googling things like "is stringify async" but I obviously was focused on stringify more than on the state aspect of things, since stringify is reasonably new to me. As I understand it, I could therefore update the localStorage as part of the callback in the setState function? – AJM Oct 06 '20 at 10:59
  • Also as an update: I tried the code you recommended and still had the same situation. – AJM Oct 06 '20 at 11:02
  • are you updating the state somewhere else as well? – Krzysztof Krzeszewski Oct 06 '20 at 11:06
0

the this.state.todoData is object. Which is passed by reference, console.log shows that. If the referenced object changes then console logged object also changes.

But, On the other hand, when u JSON.stringify it, that becomes string which is passed by value. So, it doesn't change,

    const Obj = {
      a: 1,
      b: 58,
    };

    console.log(Obj);

    Obj.a = 484151
    console.log(Obj);

Both of them logs the same thing

See console.log() shows the changed value of a variable before the value actually changes

ezio4df
  • 3,541
  • 6
  • 16
  • 31
  • I can't say I 100% understand what you mean, even after reading what you linked (I'm still a student and a lot of this is going way over my head) but I guess regardless, the natural next question is: How do I therefore access the updated state in a way which will work with stringify? – AJM Oct 06 '20 at 10:53
  • 2
    While I agree with all that, the issue here is React way of handling state updates – user0101 Oct 06 '20 at 10:53
0

(Posting solution on behalf of the question author to move it to the answer space).

It's solved! I had to create a new callback function and pass it in as a parameter to setState. I didn't realise that setState doesn't update immediately, and therefore it always felt 'one step behind'.

Updated code:

    this.setState(
      prevState => ({
        todoData: prevState.todoData.map(obj =>
          obj.id === idNum
            ? Object.assign(obj, {
                inEditMode: false,
                title: title,
                due: due,
                description: description
              })
            : obj
        )
      }),
      this.updateLocalStorage
    )
  }

and the updateLocalStorage function:

  updateLocalStorage () {
    const newState = JSON.stringify(this.state.todoData)
    localStorage.setItem('todoDataString', newState)
  }

halfer
  • 19,824
  • 17
  • 99
  • 186