DEV Community

Discussion on: Beware of these traps on state and props management using react-hooks

Collapse
 
clickclickonsal profile image
Sal Hernandez

I just want to say really good post! I like how you broke down the problem.

I wanted to point out that the useState does not mutate state, however, splice does mutate state. What you could do here is use slice which does the same thing as splice but without mutating the original array. And then you are correct that when calling setTags you want to create a new array. 😁

BTW I didn't know about that the ?. & the ?? features! Going to start using it tomorrow at work! Thanks! πŸ˜ƒ

const tagsInitialState = [...(props?.tags ?? [])]

Collapse
 
kcouliba profile image
Coulibaly Daba Kevin • Edited

Thanks for your feedback! Your explanation about splice and slice is totally right and is another way to solve the state update issue.

I have been reproducing the argument variable mutation in a straightforward example on codesandbox.io to demonstrate that useState initialization passes its argument reference to output value. I have tried to find the cause in the source code, but I'm having a hard time finding out. I would appreciate any help or insight on that. :D

The optionnal chaining operator (?.) and nullish coalescing operator (??) are both currently stage 4 proposal. Make sure you are on the latest Babel version (at least 7.8.0)
It makes code really more predictable 😁

Collapse
 
clickclickonsal profile image
Sal Hernandez • Edited

In the example, you are mutating the original array. The push method mutates the original array.

The reason why your example is working is that when you call updateComp, react triggers a re-render because there is a state change and thus your UI reflecting the changes to the array.

What you need to before updating an array is to make a copy of it so that you don't modify the original array. Then you can push/remove items in the clonedArray. and then you use the setValue method to update the value state. I reflected this in the code below using your example.

import React, { useState } from "react";
import "./styles.css";

function DisplayComp(props = {}) {
  // const [updateComp, doUpdateComp] = useState(false);
  const initialState = props.data; // that instantiation causes props to mutate
  // const initialState = [...props.data]; // that instantiation keeps props clean
  const [value, setValue] = useState(initialState);

  const handleClick = () => {
    const valueClone = [...value];
    valueClone.push((value[value.length - 1] || 1) * 2);
    setValue(valueClone)
    // doUpdateComp(!updateComp); // that line causes component to re-render
  };

  return (
    <div className="App">
      <h1>React Hooks state management</h1>
      <button onClick={handleClick}>Mutate</button>
      <p>Initial state value : {initialState.join(" ")}</p>
      <p>use state value : {value.join(" ")}</p>
    </div>
  );
}

export default function App() {
  return <DisplayComp data={[21]} />;
}