在学生(儿童)组件中
useEffect 挂钩将通过 handleStudentsChange(父组件提供的函数)更新父数组。在学生(家长)组件中
handleStudentsChange 函数使用 useCallback 挂钩定义。然而,它似乎不起作用。问题/疑问
handleStudentsChange 就会无限运行请参阅此处的代码: 我是 CodeSandBox 链接
Student.tsx(儿童)
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(父级)
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)
}
/>
);
})}
</>
);
}
如上面的代码所示,我尝试在学生(儿童)组件上使用 React.memo ,并在 handleStudentsChange 上使用 useCallback ,希望能够防止无限循环。然而,无限循环仍在继续。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号
问题
handleStudentsChange不仅在发生更改时无限运行一次-它从第一次渲染开始就无限运行。这是因为Student组件具有调用handleStudentsChange的useEffect,它更新了Students组件中的状态,导致Student组件重新渲染,然后再次调用useEffect,无限循环。解决方案
您需要在更新输入后才调用
handleStudentsChange,而不是在每次渲染后都调用。我在下面的示例中包含了一个示例,它在从输入触发blur事件后更新了Students中的状态。对于更聪明(和复杂)的方法,您可以对比props和state来决定是否需要更新,但我将让您自己解决。const { Fragment, StrictMode, useCallback, useEffect, useState } = React; const { createRoot } = ReactDOM; const { TextField } = MaterialUI; function Student(props) { const [firstName, setFirstName] = useState(props.firstName); const [lastName, setLastName] = useState(props.lastName); const [grade, setGrade] = useState(props.grade); const handleStudentsChange = props.handleStudentsChange; const onBlur = () => { handleStudentsChange(props.id, { firstName, lastName, grade, }); }; return ( <Fragment> <TextField label="firstName" onBlur={onBlur} onChange={(event) => setFirstName(event.target.value)} value={firstName} /> <TextField label="lastName" onBlur={onBlur} onChange={(event) => setLastName(event.target.value)} value={lastName} /> <TextField label="grade" onBlur={onBlur} onChange={(event) => setGrade(+event.target.value)} value={grade} /> </Fragment> ); } function Students() { const [students, setStudents] = useState([ { firstName: "Justin", lastName: "Bieber", grade: 100 }, { firstName: "Robert", lastName: "Oppenhiemer", grade: 100 } ]); const handleStudentsChange = useCallback( (index, updatedStudent) => { // console.log(index) // I only want this to rerender when the value change however it turn into an infinity loop console.log({ updatedStudent }); setStudents((prevStudents) => { const updatedStudents = [...prevStudents]; updatedStudents[index] = updatedStudent; return updatedStudents; }); }, [] ); return ( <Fragment> {students.map((student, index) => { return ( <Student key={index} id={index} firstName={student.firstName} lastName={student.lastName} grade={student.grade} handleStudentsChange={(index, newStudent) => handleStudentsChange(index, newStudent) } /> ); })} </Fragment> ); } function App() { return ( <div className="App"> <Students /> </div> ); } const root = createRoot(document.getElementById("root")); root.render(<StrictMode><App /></StrictMode>);