An Example on Avoiding Infinite Re-Rendering in React.

Problem Statement: We have an application that allows us to analyze different blockchain projects and their relevant stats. The main component of this application is a <Table /> that lists out the the top 100 projects by market capitalization in the crypto markets. Each row in this table contains an Inspect button that conditionally renders a <Details /> component in an effort to allow us to further examine the ongoings of the project. We use the useEffect hook to make a GET request to the pertinent API as a side effect of rendering the <Details /> component and set state with that data. However, how do we ensure that the appropriate fetch request is made on each render of the component without triggering an infinite loop of re-rendering?

My first attempt at building out this functionality looked something like this:

import React, { useEffect } from "react";

import { useParams } from "react-router";

function Details({ projectData, setProjectData}) {

const params = useParams();

useEffect(() => {

fetch(`${BASE_URL}/${params.id})`)

.then(resp => resp.json())

.then(data => setProjectData(data))}, [projectData])

…

Let’s walk through this code:

  • projectData is my state variable that holds the data that my <Details /> component renders and setProjectData is it’s respective setter function. This have been passed down to the <Details /> component as props.

  • params is an object that encapsulates the parameters in my URL. For the purposes of this app, when a user clicks on a project within the

    component they are directed to a URL where the last parameter is the name of the project. Thus, if the user clicks on Solana, the URL changes to home/solana. We can extract that last parameter via the id key in the params object and then use it to make the GET request.

  • resp => resp.json() converts the response from the server into JSON format.

  • data => setProjectData(data) sets state for the projectData with the data received from the server.

  • [projectData] is the dependencies array which instructs the GET to fire every time projectData changes.

Do you see the problem? πŸ™„πŸ™„πŸ™„πŸ™„πŸ™„πŸ™„πŸ™„πŸ™„

Every time the <Details /> component renders, we inherent change the projectData state. Since the dependencies array contains projectData this results in an infinite loop of GET requests.

How do we fix this? Simple, abide by the this beautiful tweet below:

post image

Our state does not synchronize with itself, that would be silly. Instead, it synchronizes most closely with the URL. Thus, the working code should take advantage of the useLocation(); hook as follows:

import React, { useEffect } from "react";

import {useParams, useLocation } from "react-router";

function Details({ projectData, setProjectData}) {

const params = useParams();

const { pathname } = useLocation();

useEffect(() => {

fetch(`${BASE_URL}/${params.id})`)

.then(resp => resp.json())

.then(data => setProjectData(data))}, [pathname])

…

BOOM πŸ˜ŽπŸ˜ŽπŸ˜ŽπŸ¦„πŸ¦„πŸ¦„πŸŒˆπŸŒˆπŸŒˆπŸΎπŸΎπŸΎπŸš€πŸš€πŸš€. No more infinite re rendering for you.