我知道,当与从数组动态创建的子组件一起使用时,特殊的“键”道具有助于 React 唯一地识别组件并有效地呈现更新。但我想知道何时以及为什么需要将 key 道具用于“非动态”组件。
我的应用程序使用 Reducer 和 useContext 挂钩来管理功能组件A 的状态。状态对象最多有 3 层嵌套。组件 A 更新状态并将状态对象的一部分作为道具传递给子组件B的两个实例。B 使用这些道具来渲染一个开关组件和两个输入组件。这是此层次结构的简化代码。
组分 A:
const A: FC = () => {
// ....
// graphql queries to get data and update state using reducer
// ...
return (
<B
enabled={data.a.b.enabled}
value1={data.a.b.value1}
value2={data.a.b.value2}
/>
<B
enabled={data.a.b.enabled}
value1={data.a.b.value1}
value2={data.a.b.value2}
/>
);
};
B组份:
const B: FC = props => {
const { value1, value2, enabled} = props; // there are other props as well
return (
<>
<div className={someClassLogic}>
<Switch
onChange={onValueChange}
isChecked={enabled}
disabled={disabled}
/>
</div>
<div className={someClassLogic} >
<Input
input={value1}
disabled={disabled}
/>
</div>
<div className={someClassLogic}>
<Input
input={value2}
disabled={disabled}
/>
</div>
</>
);
};
表格点击事件用于更新状态,组件 B 显示此选定项目的“设置”,用户可以使用组件 B 对其进行变异。
这是我面临的问题 - 当用户操作更新状态时(从表中选择一行,未在代码段中显示),我可以看到 A 和 B 在 react 开发人员工具中都收到了新数据,并且通过打印到控制台。但是,不会进行渲染以显示新数据。我想了解为什么会这样。
在查找了这个问题之后,我想我在实例化组件 B 时需要一个关键道具(答案没有清楚地解释为什么)。通过以下添加,这些值确实正确呈现。为什么这里需要一个键,为什么它只在键包含所有可以更改值的道具时才起作用?如果我只使用 uniqueId 作为键,则 value1 和 value2 不会再次正确呈现。如果我有很多更改道具,我是否也必须让他们添加密钥?没有更笨拙的方法吗?
更新的组件 A:
const A: FC = () => {
return (
<B
key={`${data.a.uniqueId}-
${data.a.b.value1}-
${data.a.b.value2}
enabled={data.a.b.enabled}
value1={data.a.b.value1}
value2={data.a.b.value2}
/>
<B
key={`${data.a.uniqueId}-
${data.a.b.value1}-
${data.a.b.value2}
enabled={data.a.b.enabled}
value1={data.a.b.value1}
value2={data.a.b.value2}
/>
);
};
另外,我注意到虽然现在单击表格行会在组件 B 中呈现正确的值,但是单击到目前为止用户未更改的行会导致先前呈现的值保留在 Input1 和 Input2 组件上(而不是的空白)。所以我必须向输入添加键以及附加的启用状态,这解决了这个问题。
更新的组件 B:
const B: FC = props => {
const { value1, value2, enabled} = props; // there are other props as well
return (
<>
<div className={someClassLogic}>
<Switch
onChange={onValueChange}
isChecked={enabled}
disabled={disabled}
/>
</div>
<div className={someClassLogic} >
<Input
key={`value1-${enabled}`}
input={value1}
disabled={disabled}
/>
</div>
<div className={someClassLogic}>
<Input
key={`value2-${enabled}`}
input={value2}
disabled={disabled}
/>
</div>
</>
);
};
再说一遍,为什么需要密钥?不反应发现道具已经改变并再次自动渲染?