0

I'm loading an array of data in my initial state of targets and in my state of dataBaseTargets, In my code I want to change the value of targets state and let the value of dataBaseTargets state unchanged after one useEffect call, but I'm encountering a problem, whenever I want to do this, the value of dataBaseTargets is changed by itself whenever I change the value of targets.

here is the sandbox of my code :

https://codesandbox.io/s/vigilant-cerf-kg2ej6?file=/src/App.js:0-963

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

export default function App() {
  const [targets, setTargets] = useState([]);
  const [dataBaseTargets, setDataBaseTargets] = useState([]);
  useEffect(() => {
    // A database call to get the dataBaseTargets and the initial value of targets
    const array = [1, 2, 3];
    setTargets(array);
    setDataBaseTargets(array);
  }, []);
  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>
      <button
        onClick={() => {
          const _targets = targets;
          _targets.push(5);
          setTargets(_targets);
        }}
      >
        {" "}
        ADD AN ELEMENT
      </button>
      <br />
      <button
        onClick={() => {
          console.log(targets); // both lines print the same value!
          console.log(dataBaseTargets);
        }}
      >
        SHOW
      </button>
    </div>
  );
}
  • 1
    You're breaking a fundamental rule of state: [Do not modify state directly](https://reactjs.org/docs/state-and-lifecycle.html#do-not-modify-state-directly). `const _targets = targets;` just makes both variables `_targets` and `targets` refer to the **same** array, it doesn't copy the array. – T.J. Crowder Oct 11 '22 at 09:28

3 Answers3

4

You are using the same array for both states, and then you mutate the array, and it reflects in both.

The const _targets = targets; doesn't clone the original array. It's just an alias to the same array. To clone it you should use something like array spread (see other methods) - const _targets = [...targets];

Example:

export default function App() {
  const [targets, setTargets] = useState([]);
  const [dataBaseTargets, setDataBaseTargets] = useState([]);
  useEffect(() => {
    // A database call to get the dataBaseTargets and the initial value of targets
    const array = [1, 2, 3];
    setTargets(array);
    setDataBaseTargets([...array]); // create a separate copy
  }, []);
  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>
      <button
        onClick={() => {
          setTargets([...targets, 5]); // create new state with the added item
        }}
      >
        {" "}
        ADD AN ELEMENT
      </button>
      <br />
      <button
        onClick={() => {
          console.log(targets);
          console.log(dataBaseTargets);
        }}
      >
        SHOW
      </button>
    </div>
  );
}
Ori Drori
  • 183,571
  • 29
  • 224
  • 209
1

In JS Objects are passed as reference and arrays are objects so both arrays are the same instance after your useEffect: target === dataBaseTargets. You should always create a new array and update the state with it instead of modifying the current value:

useEffect() => {
  const array = [1, 2, 3];
  setTargets(array.slice(0));
  setDatabaseTargets(array.slice(0));
}, []);

// Add new target:
const newTargets = targets.slice(0);
newTargets.push(target);
setTargets(newTargets);
Code Spirit
  • 3,992
  • 4
  • 23
  • 34
0

Don't try to push new elements directly to state elements, make a deep copy of variable as shown below

const _targets = JSON.parse(JSON.stringify(targets))