skip to Main Content

I currently have a component that uses a selector with useSelector from react-redux. However, after I set the state with the selector value and then update the state with a onChange event handler, they original selector value will be changed also.

How can I not mutate the original selector object when updating the state?

// Here is how the selectorObject looks

selectorObject = {
  label: "Hello"
}

const ComponentA = () => {
  const selectorObject = useSelector(getObjectSelector);
  const [stateObject, setStateObject] = useState(null);

  useEffect(() => {
    if (selectorObject) {
        setStateObject({...selectorObject})
    {
  }, [selectorObject])

  const handleOnchange = (event, key) => {
    const temp = {...selectorObject}
    // Also tried `const temp = Object.assign({}, ...selectorObject)` but same result 
    temp['label'] = event.target.value; //Assume event.target.value = "Hello World"
    setStateObject({...temp})

    console.log(stateObject)
    console.log(selectorObject)
  }

}

The two console logs in the handleOnchange function shows that both selectorObject and stateObject have been changed the same and the label proprty is equal to Hello World

What I’m expecting is for selectorObject.label to still equal Hello and stateObject.label to equal the new value Hello World

I thought the spread operator does a deep clone and should not mutate the original

2

Answers


  1. You selectorObject is a reference type (an object), spreading it to create a new object will not create a deep copy. Both stateObject and selectorObject end up pointing to the same object in memory and the situation you described happens.

    Just use lodash _.cloneDeep()

    Login or Signup to reply.
  2. I think this problem occurs because of the property of ‘…’ funtion.

    When using ‘…’, if the state object has nested objects, the nested objects will still reference the original object.

    Try to change the code like this.

    const ComponentA = () => {
      const selectorObject = useSelector(getObjectSelector);
      const [stateObject, setStateObject] = useState(null);
    
      useEffect(() => {
        if (selectorObject) {
          setStateObject(JSON.parse(JSON.stringify(selectorObject)));
        }
      }, [selectorObject]);
    
      const handleOnChange = (event, key) => {
        const temp = JSON.parse(JSON.stringify(stateObject));
        temp[key] = event.target.value; // Assume event.target.value = "Hello World"
        setStateObject(temp);
    
        console.log(stateObject);
        console.log(selectorObject);
      };
    };
    
    export default ComponentA;
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search