Maison  >  Questions et réponses  >  le corps du texte

Empêcher le rendu infini provoqué par les mises à jour des composants enfants vers le tableau de composants parent

Dans le volet étudiant (enfant)

Dans le volet étudiant (parent)

Questions/Questions

Voir 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粉197639753P粉197639753208 Il y a quelques jours416

répondre à tous(1)je répondrai

  • P粉955063662

    P粉9550636622024-02-27 00:32:58

    Question

    handleStudentsChange不仅在发生更改时无限运行一次-它从第一次渲染开始就无限运行。这是因为Student组件具有调用handleStudentsChangeuseEffect,它更新了Students组件中的状态,导致Student组件重新渲染,然后再次调用useEffect, boucle infinie.

    Solution

    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>

    répondre
    0
  • Annulerrépondre