ceaser_salad
ceaser_salad

Reputation: 5

Jsx returns object and causes infinite loop

i am learning React and I have this code snippet that builds a html table (data has 100+ object entries) by returning the necessary JSX.

For context, data is also an array of objects: [{},{},{}]

const [trafficData, setTrafficData] = useState();

getTrafficDetails().then(r => {
    setTrafficData(r);
})

<TrafficWidget data={trafficData} />

consumed in:

const TrafficWidget = ({ data }) => {
  let [trafficStatus, setTrafficStatus] = useState([]);

  if (data && data.length > 0) {
    const mapData = data.map((item) => {
      return (
        <tr key={item.id}>
          <td>{item.name}</td>
          <td>{item.totalEmission}</td>
        </tr>
      );
    });
    setTrafficStatus(mapData);
  }

  return(<tbody>{trafficStatus}</tbody>)
}

The problem that I have is that when i interrogate mapData array, it shows as {$$typeof: Symbol(react.element), type: "tr", key: "082", ref: null, props: {…}, …} and then causes a Error: Too many re-renders. React limits the number of renders to prevent an infinite loop.

Why does it come up as an object when i'm returning jsx and why would this cause an infinite loop?

Upvotes: 0

Views: 570

Answers (2)

Mbanda
Mbanda

Reputation: 1018

const { Fragment } = React

const TrafficWidget = ({
  data
}) => (
  <tbody>
    {data.map((item) => (
      <Fragment key={item.id}>
        <tr>
          <td>{item.name}</td>
          <td>{item.totalEmission}</td>
        </tr>
      </Fragment>
    ))}
  </tbody>
)


let data = [
  {
    name: "Henry",
    totalEmission: 500,
    id: "936-DEF12"
  },
  {
    name: "Nick",
    totalEmission: 200,
    id: "843-7266B"
  },
]

ReactDOM.render(<TrafficWidget data={data} />, document.getElementById('main'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<main id="main">loading or error...</main>

Upvotes: 1

user120242
user120242

Reputation: 15268

You are creating a new array object with .map each time, causing the state to change at every render call, forcing it to re-render in a loop. Re-render triggered by data changing should be enough to signal a need for re-render.
State should be used to store and mutate the state of data or something else being given to your components as props. Not for storing your component.
You want to let React do the work of reconciling changes in your components, which is what it does well.

You can just feed your mapped components directly in your render loop.

const TrafficWidget = ({ data }) => {
  // NOTE: You actually don't need to check the length here.  Empty arrays are handled like no-op.
  const trafficStatus = 
          data && data.length > 0 && data.map((item) => {
            return (
              <tr key={item.id}>
                <td>{item.name}</td>
                <td>{item.totalEmission}</td>
              </tr>
            );
          });
  return(<tbody>{trafficStatus}</tbody>);
}

Upvotes: 1

Related Questions