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">
