ค้นหาเว็บไซต์

javascript - ได้รับคำเตือน "คำเตือน: แต่ละ child ในรายการควรมี prop "key" ที่ไม่ซ้ำกัน" เมื่อ component ของฉัน render


ฉันกำลังพยายามกำจัดคำเตือนนี้ในแอป React ของฉัน:

Warning: Each child in a list should have a unique "key" prop.

Check the render method of `SettingRadioButtonGroup`. See https://reactjs.org/link/warning-keys for more information.
    at div
    at SettingRadioButtonGroup (http://localhost:3000/main_window/index.js:92979:171)
    at div
    at CardBody (http://localhost:3000/main_window/index.js:56308:27)
    at div
    at Transition (http://localhost:3000/main_window/index.js:85807:30)
    at http://localhost:3000/main_window/index.js:50905:22
    at AccordionCollapse (http://localhost:3000/main_window/index.js:49335:23)
    at div
    at Card (http://localhost:3000/main_window/index.js:49995:23)
    at div
    at SettingGroup (http://localhost:3000/main_window/index.js:92935:15)
    at TestSettingGroup (http://localhost:3000/main_window/index.js:93093:22)
    at div
    at Accordion (http://localhost:3000/main_window/index.js:49282:96)
    at div
    at ModalBody (http://localhost:3000/main_window/index.js:56308:27)
    at div
    at div
    at ModalDialog (http://localhost:3000/main_window/index.js:53383:23)
    at div
    at Transition (http://localhost:3000/main_window/index.js:85807:30)
    at Fade (http://localhost:3000/main_window/index.js:51637:24)
    at DialogTransition
    at Modal (http://localhost:3000/main_window/index.js:84195:24)
    at Modal (http://localhost:3000/main_window/index.js:53061:23)
    at App (http://localhost:3000/main_window/index.js:92660:22)

นี่คือโค้ดของคอมโพเนนต์ SettingRadioButtonGroup ของฉัน:

import { nanoid } from "nanoid";
import React, { useState } from "react";
import { Form, InputGroup } from "react-bootstrap";

export type RadioButtonGroup = {
  desc: string;
  isSelected: boolean;
};

export interface SettingRadioButtonGroupProps
{
  groupDesc: string;
  radioButtons: RadioButtonGroup[];
  onRadioSelected: (selectedItem: string) => void;
};

export function SettingRadioButtonGroup(props: SettingRadioButtonGroupProps)
{
  const radioGroupName = nanoid();
  return (
    <div className="radio-group">
      <p className="radio-label">{props.groupDesc}</p>
      <div className="group">
        {
          props.radioButtons.map((button) =>
          {
            if (button.isSelected)
              props.onRadioSelected(button.desc);

            const buttonLabel = nanoid();
            return (
              <div className="radio-button">
                <input type="radio" id={buttonLabel} name={radioGroupName} onClick={() => { () => props.onRadioSelected(button.desc) }} />
                <label htmlFor={radioGroupName}>{button.desc}</label>
              </div>
            );
          })
        }
      </div>
    </div>
  );
}

นี่คือส่วนประกอบที่สร้างส่วนประกอบเหล่านี้:

import { useState } from "react";
import { TextBoxSetting } from "./Components/TextBoxSetting";
import { SettingGroup } from "./Components/SettingGroup";
import SettingGroupBaseProps from "./Components/SettingGroupBaseProps";
import { RadioButtonGroup, SettingRadioButtonGroup } from "./Components/SettingRadioButtonGroup";
import faker from  "faker/locale/fr_CA";

export default function TestSettingGroup(props: SettingGroupBaseProps)
{
  const [textInput, setTextInput] = useState<string>();

  const radioButton: RadioButtonGroup[] = [];
  for (let i = 0; i <= 8; i++) {
    radioButton.push({
      desc: faker.lorem.word(),
      isSelected: false
    })
  }

  const [buttonSelected, setButtonSelected] = useState<string>();
  import("lodash")
    .then((_) =>
    {
      const selected = _.find(radioButton, (value) => value.isSelected);
      if (selected)
        setButtonSelected(selected.desc);
    })
    .catch();



  function onSave()
  {

    const settings = {
      textBoxSetting: textInput,
      radioButtonSelected: buttonSelected
    };

    console.table(settings);
  }


  return (
    <SettingGroup name={props.name} desc={props.desc} onSave={onSave}>
      <TextBoxSetting onTextEntered={(e) => setTextInput(e)} name="Text box setting component" />
      <SettingRadioButtonGroup groupDesc={"Radio button group"} radioButtons={radioButton} onRadioSelected={setButtonSelected} />
    </SettingGroup>
  );
}

ฉันพยายามที่จะกำจัดคำเตือนโดยการกำหนด id แบบสุ่มให้กับ property ของ key ดังนี้ แต่ฉันยังคงมีคำเตือนอยู่ และมันทำให้คอมโพเนนต์เกิดข้อบกพร่องที่สร้างปุ่มวิทยุใหม่เมื่อฉันคลิกปุ่มวิทยุ นอกจากนี้ยังสร้างคำเตือนดังต่อไปนี้ด้วย:

Warning: Encountered two children with the same key, `tvehx36gLi-hG56z68RTg`. Keys should be unique so that components maintain their identity across updates. Non-unique keys may cause children to be duplicated and/or omitted — the behavior is unsupported and could change in a future version.
    at div
    at div
    at div
    at SettingRadioButtonGroup (http://localhost:3000/main_window/index.js:92980:171)
    at div
    at CardBody (http://localhost:3000/main_window/index.js:56308:27)
    at div
    at Transition (http://localhost:3000/main_window/index.js:85807:30)
    at http://localhost:3000/main_window/index.js:50905:22
    at AccordionCollapse (http://localhost:3000/main_window/index.js:49335:23)
    at div
    at Card (http://localhost:3000/main_window/index.js:49995:23)
    at div
    at SettingGroup (http://localhost:3000/main_window/index.js:92935:15)
    at TestSettingGroup (http://localhost:3000/main_window/index.js:93094:22)
    at div
    at Accordion (http://localhost:3000/main_window/index.js:49282:96)
    at div
    at ModalBody (http://localhost:3000/main_window/index.js:56308:27)
    at div
    at div
    at ModalDialog (http://localhost:3000/main_window/index.js:53383:23)
    at div
    at Transition (http://localhost:3000/main_window/index.js:85807:30)
    at Fade (http://localhost:3000/main_window/index.js:51637:24)
    at DialogTransition
    at Modal (http://localhost:3000/main_window/index.js:84195:24)
    at Modal (http://localhost:3000/main_window/index.js:53061:23)
    at App (http://localhost:3000/main_window/index.js:92660:22)
overrideMethod @ react_devtools_backend.js:2430
printWarning @ react-dom.development.js:67
error @ react-dom.development.js:43
warnOnInvalidKey @ react-dom.development.js:13620
reconcileChildrenArray @ react-dom.development.js:13651
reconcileChildFibers @ react-dom.development.js:14125
reconcileChildren @ react-dom.development.js:16990
updateHostComponent @ react-dom.development.js:17632
beginWork @ react-dom.development.js:19080
beginWork$1 @ react-dom.development.js:23940
performUnitOfWork @ react-dom.development.js:22776
workLoopSync @ react-dom.development.js:22707
renderRootSync @ react-dom.development.js:22670
performSyncWorkOnRoot @ react-dom.development.js:22293
(anonymous) @ react-dom.development.js:11327
unstable_runWithPriority @ scheduler.development.js:646
runWithPriority$1 @ react-dom.development.js:11276
flushSyncCallbackQueueImpl @ react-dom.development.js:11322
flushSyncCallbackQueue @ react-dom.development.js:11309
discreteUpdates$1 @ react-dom.development.js:22420
discreteUpdates @ react-dom.development.js:3756
dispatchDiscreteEvent @ react-dom.development.js:5889
9react_devtools_backend.js:2430 Warning: Encountered two children with the same key, `wFq9eikLw6B_LN98bUqCV`. Keys should be unique so that components maintain their identity across updates. Non-unique keys may cause children to be duplicated and/or omitted — the behavior is unsupported and could change in a future version.
    at div
    at div
    at div
    at SettingRadioButtonGroup (http://localhost:3000/main_window/index.js:92980:171)
    at div
    at CardBody (http://localhost:3000/main_window/index.js:56308:27)
    at div
    at Transition (http://localhost:3000/main_window/index.js:85807:30)
    at http://localhost:3000/main_window/index.js:50905:22
    at AccordionCollapse (http://localhost:3000/main_window/index.js:49335:23)
    at div
    at Card (http://localhost:3000/main_window/index.js:49995:23)
    at div
    at SettingGroup (http://localhost:3000/main_window/index.js:92935:15)
    at TestSettingGroup (http://localhost:3000/main_window/index.js:93094:22)
    at div
    at Accordion (http://localhost:3000/main_window/index.js:49282:96)
    at div
    at ModalBody (http://localhost:3000/main_window/index.js:56308:27)
    at div
    at div
    at ModalDialog (http://localhost:3000/main_window/index.js:53383:23)
    at div
    at Transition (http://localhost:3000/main_window/index.js:85807:30)
    at Fade (http://localhost:3000/main_window/index.js:51637:24)
    at DialogTransition
    at Modal (http://localhost:3000/main_window/index.js:84195:24)
    at Modal (http://localhost:3000/main_window/index.js:53061:23)
    at App (http://localhost:3000/main_window/index.js:92660:22)
import { nanoid } from "nanoid";
import React, { useState } from "react";
import { Form, InputGroup } from "react-bootstrap";
import { RadioButtonGroup } from "./SettingRadioButtonGroup";


export interface SettingCheckboxButtonGroupProps
{
  groupDesc: string;
  checkboxButtons: RadioButtonGroup[];
  onCheckboxSelected: (selectedItem: string) => void;
};

export function SettingChecboxButtonGroup(props: SettingCheckboxButtonGroupProps)
{
  const radioGroupName = nanoid();
  return (
    <div className="radio-group">
      <p className="radio-label">{props.groupDesc}</p>
      <div className="group">
        {
          props.checkboxButtons.map((button) =>
          {
            if (button.isSelected)
              props.onCheckboxSelected(button.desc);

            const buttonLabel = nanoid();
            return (
              <div className="radio-button">
                <input key={radioGroupName} type="checkbox" id={buttonLabel} name={radioGroupName} onClick={() => { () => props.onCheckboxSelected(button.desc); }} />
                <label key={radioGroupName} htmlFor={radioGroupName}>{button.desc}</label>
              </div>
            );
          })
        }
      </div>
    </div>
  );
}

คำตอบ #1

คุณต้องใส่คีย์บนองค์ประกอบที่อยู่นอกสุดใน map

คีย์ขององค์ประกอบช่วยแยกองค์ประกอบออกจากพี่น้องของมันเมื่อเทียบกับพ่อแม่ของพวกมัน ที่นี่ div คือพี่น้องที่ต้องได้รับการแยกแยะ ไม่ใช่องค์ประกอบ input หรือ label

ตัวอย่าง :

<div key={radioGroupName} className="radio-button">
    <input type="checkbox" id={buttonLabel} name={radioGroupName} onClick={() => { () => props.onCheckboxSelected(button.desc); }} />
    <label htmlFor={radioGroupName}>{button.desc}</label>
</div>

นอกจากนี้คีย์ยังทำหน้าที่ให้กับองค์ประกอบที่สร้างขึ้นแบบไดนามิก (ในลูป/map) มีเอกลักษณ์ที่มั่นคง ดังนั้นผมไม่แนะนำให้ใช้คีย์ที่สร้างแบบสุ่ม เว้นแต่คุณจะเก็บคีย์เหล่านั้นใน state เพื่อให้มั่นคงเช่นกัน (ถ้าคีย์ทั้งหมดถูกสร้างใหม่และเปลี่ยนทุกครั้งที่มีการ rerender ก็ไม่มีประโยชน์ในการกำหนดคีย์) ดูเอกสารเกี่ยวกับคีย์เพื่อข้อมูลเพิ่มเติมได้

คำตอบ #2

ใน React ทุกครั้งที่คุณกำลังจะแสดงลำดับของรายการจากอาเรย์ เช่น ใช้ฟังก์ชัน map คุณต้องให้ key ที่ไม่ซ้ำกัน สำหรับแต่ละรายการเพื่อช่วยให้เอนจินของ React จำแนกแต่ละรายการได้ ในตัวอย่างของคุณ คุณสามารถทำบางอย่างเช่นนี้เพื่อแก้ไข:

props.radioButtons.map((button, index) =>
  {
    if (button.isSelected)
      props.onRadioSelected(button.desc);

    const buttonLabel = nanoid();
    return (
      <div key={buttonLabel} className="radio-button">
        <input type="radio" id={buttonLabel} name={radioGroupName} onClick={() => { () => props.onRadioSelected(button.desc) }} />
        <label htmlFor={radioGroupName}>{button.desc}</label>
      </div>
    );
  })
}

ตรวจสอบการเปลี่ยนแปลงที่ <div key={buttonLabel} className="radio-button">