Cegah pemaparan semula tanpa had yang disebabkan oleh kemas kini komponen anak kepada tatasusunan komponen induk
P粉197639753
P粉197639753 2024-02-26 10:26:24
0
1
497

Dalam komponen pelajar (kanak-kanak)

  • Apabila nilai pembolehubah berubah, useEffect 挂钩将通过 handleStudentsChange(fungsi yang disediakan oleh komponen induk) mengemas kini tatasusunan induk.

Dalam komponen pelajar (ibu bapa)

  • Bentangkan senarai komponen pelajar (kanak-kanak)
  • Untuk mengelakkan gelung tak terhingga, handleStudentsChange 函数使用 useCallback definisi cangkuk. Namun, ia nampaknya tidak berkesan.

Soalan/Soalan

  • Setelah perubahan dibuat, handleStudentsChange akan berjalan selama-lamanya
  • Kenapa ni? dan bagaimana untuk memperbaikinya?
  • Nota: Saya tidak memerlukan butang onSubmit

Lihat kod di sini: Saya adalah pautan CodeSandBox

Pelajar.tsx(Anak)

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

Pelajar.tsx(ibu bapa)

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

Seperti yang ditunjukkan dalam kod di atas, saya cuba menggunakan React.memo pada komponen pelajar (kanak-kanak) dan useCallback pada React.memo ,并在 handleStudentsChange 上使用 useCallback dengan harapan dapat menghalang gelung tak terhingga. Walau bagaimanapun, gelung tak terhingga berterusan.

P粉197639753
P粉197639753

membalas semua(1)
P粉955063662

Soalan

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

Penyelesaian

Anda perlu memanggil negeri dalam handleStudentsChange,而不是在每次渲染后都调用。我在下面的示例中包含了一个示例,它在从输入触发blur事件后更新了Students hanya selepas mengemas kini input. Untuk pendekatan yang lebih bijak (dan lebih kompleks), anda boleh membandingkan prop dan keadaan untuk memutuskan sama ada kemas kini diperlukan, tetapi saya akan membenarkan anda memikirkannya sendiri.

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>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan