简体   繁体   中英

What is the best practice to setState() a class object in React?

So let's assume I have this code

import {useState} from 'react'

class Something{
   counter:number
   constructor(counter:number){
      this.counter = counter
   }
}

function functionalComponent(){
   const [state, setState] = useState<Something>(new Something(5))

   //other codes .....

   function changeState(){
   //see below
   }

   return (
      <>
        <h1>{state.counter}</h1>
        <button onClick={changeState}>Increment</button>
      </>
   )
}

export default functionalComponent

Now at some point, I want to change the state to have counter increment 1, the below code works but...

   function changeState(){
      state.counter += 1
      setState(state)
   }

And before you all start shouting in the comments, I know that we shouldn't mutate the state directly and instead create a new state and pass it on to the setState. But the thing is for js objects and arrays we can simply use the spread operators, but I couldn't find anything simpler for class objects.

So my question is " What should be the best approach for situations like this? "

One I could think of and is using

   function changeState(){
      let newState = new Something(state.counter+1)
      setState(newState)
   }

But it becomes counter productive if my object contains many fields say 15

You can use your class inside an object and use the spread operator:

const [state, setState] = useState({state:new Something(5)})

state.state.counter = 1
setState({...state})

While it would be technically possible to create a new Something every time you want to change the state:

const [state, setState] = useState(5)
const something = useMemo(() => new Something(state), [state]);

I'd also first strongly consider whether you need a class inside a functional component at all - it's pretty odd. There's probably a more intuitive solution.

Another option which may or may not work depending on what your actual code contains in the class constructor would be to have the state contain an object for all instance properties:

const [state, setState] = useState({ counter: 5 });
const something = useMemo(() => Object.assign(new Something(), state), [state]);

Lets just use useState and functional components it would make your life way easier..

One Approach that I tried and seems to be close enough is to have a clone function in the class that returns a clone(new Instance). See the below example for a better understanding

class Something{
   counter:number
   constructor(counter:number){
      this.counter = counter
   }
   
   clone(){
      return new Something(this.counter)
   }
}

So this way whenever I'll call clone it will return a new Instance. Now in the changeState function, we can do

   function changeState(){
      let newState = state.clone()
      newState.counter += 1
      setState(newState)
   }

These steps ensures that no where the data is mutated as adheres to the principal of React.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM