In the student (child) component
useEffect
hook will update the parent array via handleStudentsChange
(the function provided by the parent component). In the student (parent) component
handleStudentsChange
function uses the useCallback
hook definition. However, it doesn't seem to work. Questions/Questions
handleStudentsChange
will run indefinitelySee the code here: I am CodeSandBox link
Student.tsx(Children)
import React, { useState, useEffect, useRef } from "react"; import TextField from "@mui/material/TextField"; interface student { firstName: string; lastName: string; grade: number; } interface studentProps { id: number; firstName: string; lastName: string; grade: number; handleStudentsChange: (index: number, student: student) => void; } function Student(props: studentProps) { const [firstName, setFirstName] = useState(props.firstName); const [lastName, setLastName] = useState(props.lastName); const [grade, setGrade] = useState(props.grade); useEffect(() => { handleStudentsChange(id, { firstName: firstName, lastName: lastName, grade: grade }); }, [firstName, lastName, grade, props]); return ( <> <TextField label="firstName" onChange={(event) => setFirstName(event.target.value)} value={firstName} /> <TextField label="lastName" onChange={(event) => setLastName(event.target.value)} value={lastName} /> <TextField label="grade" onChange={(event) => setGrade(+event.target.value)} value={grade} /> </> );
Students.tsx(parent)
import React, { useState, useCallback } from "react"; import Student from "./Student"; interface student { firstName: string; lastName: string; grade: number; } export default function Students() { const [students, setStudents] = useState<student[]>([ { firstName: "Justin", lastName: "Bieber", grade: 100 }, { firstName: "Robert", lastName: "Oppenhiemer", grade: 100 } ]); const handleStudentsChange = useCallback( (index: number, updatedStudent: student) => { // console.log(index) //I only want this to rerender when the value change however it turn into an infinity loop setStudents((prevStudents) => { const updatedStudents = [...prevStudents]; updatedStudents[index] = updatedStudent; return updatedStudents; }); }, [] ); return ( <> {students.map((student, index) => { return ( <Student key={index} id={index} firstName={student.firstName} lastName={student.lastName} grade={student.grade} handleStudentsChange={(index: number, newStudent: student) => handleStudentsChange(index, newStudent) } /> ); })} </> ); }
As shown in the code above, I tried using React.memo
on the student (child) component and useCallback
on the handleStudentsChange
, hopefully Able to prevent infinite loops. However, the infinite loop continues.
question
handleStudentsChange
doesn't just run once infinitely when a change occurs - it runs infinitely from the first render. This is because theStudent
component has auseEffect
that callshandleStudentsChange
, which updates the state in theStudents
component, causingStudent
The component re-renders and then callsuseEffect
again, infinite loop.solution
You need to call
handleStudentsChange
only after updating the input, not after every render. I've included an example below that updates the state in Studentsafter the
blurevent
is fired from the input. For a smarter (and more complex) approach, you could compare props and state to decide if an update is needed, but I'll let you figure that out yourself.