0

Why it's looping if i use { data: {}} when all fine with { data: 1} ?

Codesandbox example.

import React, { useEffect, useState } from "react";
import ReactDOM from "react-dom";

import "./styles.css";

function App() {
  const [test, setTest] = useState(true);

  const role = useRole({ data: {} }); // with object { data: 1 } all fine

  useEffect(() => {
    setTest(false);
  }, []);

  return 1;
}

export function useRole({ data }) {
  const [role, roleSet] = useState(false);

  useEffect(() => {
    console.log("looping");
    roleSet({});
  }, [data]);

  return role;
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
ZiiMakc
  • 31,187
  • 24
  • 65
  • 105

2 Answers2

1

I assume that this has to do with how javascript works.

In react, the useEffect runs after every render by default. A way to customize that behavior is by providing a list as the second parameter.

That way react will check after a render against the provided values from the previous render and call the effect only when the values have changed.

This is where the problem comes in as you may verify with the example below:

if(1 === 1) {
    console.log("1 === 1");
}

if({} === {}) {
    console.log("{} === {}");
}

If you run this you may notice an output of only 1 === 1 here. That is because javascript is not treating two empty objects as equal.

Since you are providing { data: {} } and unpack the value of data in your useRole you have an empty object in your list.

I hope that helps.

nullchimp
  • 735
  • 4
  • 13
  • But it's the same with not empy object { data: {1:1} }, or i don't get something? – ZiiMakc May 13 '19 at 23:50
  • 1
    Object comparison in javascript is not that simple. You may want to have a look at [this](https://stackoverflow.com/questions/1068834/object-comparison-in-javascript) – nullchimp May 13 '19 at 23:55
1

I think what @TvG told is right on spot. Object comparison are done via references. When you are creating the object the way you have done in the code will create new reference object everytime.

const role = useRole({ data: {} });

Even if you do it like this:

let defaultRole = { data: {} }
const role = useRole(defaultRole);

It will be creating the new object every time . the value of defaultRole will be recalculated in every render.

What can be done here is , React provides us useRef method which will not change on rerenders unless changed explicitly. Here is the link for you to read:

useRef docs You can do something like this:

const { useEffect, useState, useRef }  = React


function App() {
  const [test, setTest] = useState(true);
  console.log("running this")
  let baseObj = {
    data: {}
  }
  const roleDefaultValueRef = useRef(baseObj)
  const role = useRole(roleDefaultValueRef.current);
  useEffect(() => {
    setTest(false);
  }, []);

  return 1
}

function useRole({ data }) {
  const [role, roleSet] = useState(false);

  useEffect(() => {
    console.log("looping");
    roleSet({});
    debugger
  }, [data]);

  return role;
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.6/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.6/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Folllow up question why object like data = {} will work in this case. SO your useRole has an effect which is dependent on data. Let say you called useRole as useRole({})

now in your useRole you are spreading data value out of this passed object {} so this line

export function useRole({ data }) {

// data will be evaluated as undefined and undefined will remain same on //consecutive rerenders and hence the effect will not run 
}

This is the reason it is running when you are passing a blank object to useRole.

Hope this helps. FOr understanding try to print the value of data in useRole, you will definitely understand it :)

1 === 1 //true
undefined === undefined //true
{} === {} // false
simbathesailor
  • 3,681
  • 2
  • 19
  • 30