0

this has to be one of the worst encounters of a bug I've ever come across. The code we're working with consists of grabbing data from the database and creating two separate states to hold the incoming data, one containing the original data, and the other to mutate that data. I created these two separate data sets to compare each other and let the user know they're able to publish changes.

Below, you'll see in the console that both of the states start out null, then receive the basic data from the back-end, and once I added a product, both of the states updated with that product... but only one is supposed to update. You'll think I'm crazy until you see the code

enter image description here

This is where the data is initialized (index.js):

  const [initialData, setInitialData] = useState(null);
  const [data, setData] = useState(null);
  const [initial, setInitial] = useState(false);

  const [newChanges, setNewChanges] = useState(false);

  useEffect(() => {
    if(isEqual(data, initialData)) {
      setNewChanges(false);
    } else {
      setNewChanges(true);
    }
  }, [data, setData, initialData, setInitialData])

  useEffect(() => {
    console.log("Editable Data:");
    console.log(data);
    console.log(' ');
    console.log('Initial Data');
    console.log(initialData);
  }, [data, setData, initialData, setInitialData])

  useEffect(() => {
    if(initial === true) return;
    if(session === undefined) return;
    if(status === 'unauthenticated') {
      router.push('/');
      return;
    }
    if(status === 'loading') return;
    if(data !== null) return;
    if(initialData !== null) return;

    async function dashboardData() {
      setInitial(true);
      
      const result = await axios.post(`${window.location.origin}/api/admin/dashboard`, {
        id: session.user.token.id,
        username: session.user.token.username
      }).catch((error) => {
        alert(error.toString())
      })
      if(result !== undefined) {
        if(result.status === 200) {
          const resData = result.data;
          setData(resData);
          setInitialData(resData);
          console.log("Data retrieved from database");
        } else {
          alert("Connection error to the database... please try again later.");
        }
      }
    }

    dashboardData();
    

  }, [session, status])

Below is how I'm passing the data and the setData function to the Products component as props:

<Products data={data} setData={setData} />

Below is how the changes are actually applied:

export default function Products({ data, setData }) {

  const products = data.products;

  const [allProducts, setAllProducts] = useState(products);
  const [displayProducts, setDisplayProducts] = useState(products);

  function updateProducts(newArray) {
    setAllProducts(newArray);
    setDisplayProducts(newArray);

     setData({...data, products: newArray})
  }
}

Absolutely no where is setInitialData being called in the rest of the classes, it's not being passed down to any components, it's strictly called upon the database pull.

  • `You pass the same reference to `setData` and `setInitialData``. The you most likely edit the `data` directly at some point so `initialData` is modified as well – Konrad Apr 06 '23 at 20:50
  • @Konrad In the useEffect API call, the data from the back-end is declared as (const data = result.data). There's no way the data from the back-end is being edited on the front-end and then mutated to be set again. This is a one-time pull from the back-end :/ – Joshua Feliciano Apr 06 '23 at 21:03
  • Could you show the entire `Products` component? – Konrad Apr 06 '23 at 22:03
  • Changing `setInitialData(resData)` to `setInitialData(structuredClone(resData))` should help – Konrad Apr 06 '23 at 22:14
  • @Konrad This fixed my issue, however, I have the same codes on another application that doesn't require the structuredClone method... any guesses as to why this could be? Thank you – Joshua Feliciano Apr 06 '23 at 22:41
  • As I said in the previous comment - you modify the object somewhere, probably in `Products` component – Konrad Apr 06 '23 at 22:52
  • @Konrad So I edited the [data] state of the data coming from the API. The API data is copied into the two states, [data] and [initialData]. I modify ONLY the [data] state which then modifies the [initialData] state, so I'm just confused on how those two separate states share changes. – Joshua Feliciano Apr 06 '23 at 22:55
  • @Konrad I never call [setInitialData] anywhere in the Products component. I only use that function when applying the data from the API request. – Joshua Feliciano Apr 06 '23 at 23:01
  • Doing something like `data.products.push` will modify initial data as well, see https://stackoverflow.com/questions/518000/is-javascript-a-pass-by-reference-or-pass-by-value-language – Konrad Apr 06 '23 at 23:02

1 Answers1

0

I read the text many times and saw the picture, but nothing came to my mind. I think the best way you can find your bug is to change the setstate, I mean change the "setData" to "setInitialData" passing the prop

<Products data={data} setData={setInitialData} />

and then check the console log if the setData changes or not