PazzeG
PazzeG

Reputation: 137

React.js infinite re-render

i got a issue with my code. My function "getNames" rerender may times, but i want it to render once? have you got any clue ?

import grounds  from './../../UballersGroundsData.json';

export default function Groundlist() {

    function getNames(jsonObj){
        for(let item in jsonObj){
            console.log("item = " + item);
            for(let property in jsonObj[item] ){
                console.log(jsonObj[item]);
                // if (property === "groundName"){
                //     console.log(jsonObj[item][property]);
                // }
                
            }
        }       
    }

return(
        <div>
            <h1>Hello world!</h1> 
            <ul>
                {getNames(grounds)}
            </ul>

        </div>
    )
}

Thank you !

Upvotes: 1

Views: 186

Answers (4)

You should never call a function inside return scope of render. It's normal for a component to re-render without proper treatment.

Taking in mind the other 2 answers

You can use the full power of useEffect, useCallback and React.memo to prevent anything from re-render.

import React from 'react';
import grounds  from './../../UballersGroundsData.json';

function Groundlist() {
  // initiate state
  const [names, setNames] = React.useState([]);

  // This will prevent the Function from recalculate - useCallback
  const getNames = React.useCallback(function (jsonObj) {
    for(let item in jsonObj){
      console.log("item = " + item);
      for(let property in jsonObj[item] ){
        console.log(jsonObj[item]);
        // if (property === "groundName"){
        //     console.log(jsonObj[item][property]);
        // }
      }
    }
  }, []);
  

  // Will make function run only once and nevermore - useEffect
  React.useEffect(() => {
    setNames(getNames());
  }, [])

  return(
    <div>
        <h1>Hello world!</h1>
        <ul>
            {names.map(a => <li>{a}</li>)}
        </ul>

    </div>
  )
}

// Will prevent React from try to re-render without changing in props, so as your component has no props, will never re-render without yourself unmounting first
export default React.memo(Groundlist);

In another cases you can control exact when the component should recalculate your names using the last argument of functions

useCallback(() => {}, []) //<---

For example

useCallback(() => {}, [updateState]);

when updateState change the function will be recreated.

Upvotes: 0

shiponcs
shiponcs

Reputation: 1677

Try using useMemo and useCallBack you want to optimize your react app. React Official docs clearly described how to use it: useMemo useCallBack

Upvotes: 0

Ajeet Shah
Ajeet Shah

Reputation: 19813

You can use useMemo react hook to memoize the returned value i.e. skip unnecessary / heavy calculations due to change in other state, props or context variables.

Example:

import { useMemo } from "react"

export default function Groundlist(props) {

  const grounds = props.data // if grounds is passed as props from Parent component

  const groundsMemo = useMemo(() => {
    // do all the heavy calculations here 
    // (e.g. do the work of getNames function)
    
    // and return some JSX or Array (data)

    // returned value will be memoized;
    // means it will be re-calculated only if "grounds" changes
    
    // Hence, no unnecessary calls to getNames (heavy calculations)

  }, [grounds])

  return (
    <div>
      {/* Use this if groundsMemo is JSX */}
      <ul>{groundsMemo}</ul>

      {/* Use this if groundsMemo is an Array (data) */}
      <ul>{groundsMemo.map(item => <li key={some_key}>
        {item.property}
      </li>)}</ul>
    </div>
  )
}

Upvotes: 1

jharris711
jharris711

Reputation: 612

You should put your function inside of a useEffect hook, then set it to a state hook with useState. Then, map out the list items for your list (assuming you are returning an array from your function). If you want it to only run getNames on the first render, you would set it up the useEffect hook with an empty dependency array. Code should look something like this:

import React, { useEffect, useState } from 'react'
import grounds  from './../../UballersGroundsData.json';

export default function Groundlist() {
    const [names, setNames] = useState([]) // Initial state with empty array

    useEffect(() => {
        function getNames(jsonObj){
            // your function logic here...       
        }
        const result = getNames(grounds) // Call your function
        setNames(result) // set it to names state hook
    }, []) // Empty array here means it will only use the useEffect on the first render.

return(
        <div>
            <h1>Hello world!</h1> 
            <ul>
                {Array.from(names).map(name =>  <li>{name}</li>)}
            </ul>

        </div>
    )
}

Upvotes: 1

Related Questions