Maison > Questions et réponses > le corps du texte
Dans le volet étudiant (enfant)
useEffect
挂钩将通过 handleStudentsChange
(fonction fournie par le composant parent) met à jour le tableau parent. Dans le volet étudiant (parent)
handleStudentsChange
函数使用 useCallback
définition du crochet. Cependant, cela ne semble pas fonctionner. Questions/Questions
handleStudentsChange
fonctionnera indéfinimentVoir le code ici : Je suis le lien CodeSandBox
Étudiant.tsx(Enfant)
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} /> </> );
Étudiants.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) } /> ); })} </> ); }
Comme indiqué dans le code ci-dessus, j'ai essayé d'utiliser React.memo
sur le composant étudiant (enfant) et useCallback
sur React.memo
,并在 handleStudentsChange
上使用 useCallback
dans l'espoir d'éviter une boucle infinie. Cependant, la boucle infinie continue.
P粉9550636622024-02-27 00:32:58
handleStudentsChange
不仅在发生更改时无限运行一次-它从第一次渲染开始就无限运行。这是因为Student
组件具有调用handleStudentsChange
的useEffect
,它更新了Students
组件中的状态,导致Student
组件重新渲染,然后再次调用useEffect
, boucle infinie.
Vous devez appeler l'état dans handleStudentsChange
,而不是在每次渲染后都调用。我在下面的示例中包含了一个示例,它在从输入触发blur
事件后更新了Students
seulement après avoir mis à jour l'entrée. Pour une approche plus intelligente (et plus complexe), vous pouvez comparer les accessoires et l'état pour décider si une mise à jour est nécessaire, mais je vous laisse le découvrir vous-même.
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>);
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script> <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script> <script crossorigin src="https://unpkg.com/@mui/material@latest/umd/material-ui.production.min.js"></script> <div id="root"></div>