Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

React state resets when used with a composite object

I’m new to React. Here is the scenario:

  • I have a class called Book. A book has a title and content, both strings.
  • I’m trying to separate the concerns within the book page into a BookComponent (view) and Book hook (state) and a Book class (dealing with the backend API), similar to what’s described here: https://martinfowler.com/articles/modularizing-react-apps.html

Here is a sample code:

export const useBook = () => {
  const [book, setBook] = React.useState<Book>(new Book('', ''))
 
  function load(id: string) {
    Book.fetch(id).then((book) => { setBook(book) })
  }
  
  function title() : string {
     return book.title
  }

  function content() : string {
     return book.content 
  }

  function setTitle(title: string) {
    setBook({ ...book, title })
  }

  function setContent(content: string) {
    setBook({ ...book, content })
  }

  return {
    title,
    setTitle,
    content,
    setContent
  }
}

In the BookComponent (view) I have this:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

<div>{book.title()}</div>
<input type="text" value={book.content()} onChange={(e) => book.setContent(e.target.value) }} />

(Omitted useEffect code to load the book)

The page shows the title and the content correctly. However, as soon as I type anything in the textbox, the title goes back to "" which is the initial value of the state.

I thought using setBook({...book, content}) within the hook is going to copy the existing book state and only modify content, but it seems the code enters setContent with a brand new book when I inspect ...book

Changing the state to the following fixes the problem:

const [book, setBook] = React.useState<Book | null>(null)
// ...
function title() : string {
 if (!book) return { '' }
 
 return book.title
}
// same for content

But I don’t understand why the state would start from a null value (default) every time and the lifecycle of it.

>Solution :

Set your new state like this (with a callback fn) inside your setContent (and similarly in setTitle) with an access to previous state and it should work exactly as you expected. Copy prevState with spread operateor into a new state and override content:

setBook(prevState => ({ ...prevState, content }))
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading