Verhindern Sie ein unendliches erneutes Rendern, das durch Aktualisierungen untergeordneter Komponenten im Array übergeordneter Komponenten verursacht wird
P粉197639753
P粉197639753 2024-02-26 10:26:24
0
1
495

In der Schüler-(Kind-)Komponente

  • Wenn sich der Wert der Variablen ändert, useEffect 挂钩将通过 handleStudentsChange(von der übergeordneten Komponente bereitgestellte Funktion) aktualisiert das übergeordnete Array.

In der Schüler-(Eltern-)Komponente

  • Präsentieren Sie eine Liste der Schüler-(Kind-)Komponenten
  • Um Endlosschleifen zu verhindern, handleStudentsChange 函数使用 useCallback Hook-Definition. Allerdings scheint es nicht zu funktionieren.

Fragen/Fragen

  • Sobald eine Änderung vorgenommen wird, handleStudentsChangewird sie auf unbestimmte Zeit ausgeführt
  • Warum ist das so? und wie kann man es beheben?
  • Hinweis: Ich benötige den OnSubmit-Button nicht

Sehen Sie sich den Code hier an: Ich bin CodeSandBox-Link

Student.tsx(Kind)

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(Elternteil)

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)
            }
          />
        );
      })}
    </>
  );
}

Wie im Code oben gezeigt, habe ich versucht, React.memo für die Schülerkomponente (untergeordnete Komponente) und useCallback für React.memo ,并在 handleStudentsChange 上使用 useCallback zu verwenden, in der Hoffnung, eine Endlosschleife zu verhindern. Die Endlosschleife geht jedoch weiter.

P粉197639753
P粉197639753

Antworte allen(1)
P粉955063662

问题

handleStudentsChange不仅在发生更改时无限运行一次-它从第一次渲染开始就无限运行。这是因为Student组件具有调用handleStudentsChangeuseEffect,它更新了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>);
<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>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage