Как свойство key работает в нединамическом дочернем компоненте и почему оно важно для рендеринга обновлений?
Я понимаю, что специальная опция key при использовании с дочерними компонентами, которые создаются динамически из массивов, помогает React однозначно идентифицировать компоненты и эффективно отображать обновления. Но я хотел бы знать, когда и почему необходимо использовать ключевую опору для «нединамического» компонента.
В моем приложении для управления состоянием функционального компонента A используются обработчики Reducer и useContext . Объект состояния имеет максимум 3 уровня вложенности. Компонент A обновляет состояние и передает часть государственного объекта в качестве реквизита до двух экземпляров ребенка компонента B . B использует эти реквизиты для рендеринга компонента переключателя и двух компонентов ввода. Вот упрощенный код этой иерархии.
Компонент А:
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>
</>
);
};
Событие щелчка tablerow используется для обновления состояния, и компонент B отображает «настройки» этого выбранного элемента, которые пользователь может изменить с помощью компонента B.
Вот проблема, с которой я столкнулся: когда состояние обновляется действием пользователя (выбор строки из таблицы, не показанной во фрагменте), я вижу, что как A, так и B получают новые данные в средствах разработки реагирования и путем печати на консоли. Но рендеринг для отображения новых данных не выполняется. Я хотел бы понять, почему это так.
Изучив эту проблему, я решил, что мне нужна ключевая опора при создании экземпляра компонента 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>
</>
);
};
Опять же, зачем нужен ключ? Не реагирует, выясняет, что реквизит изменился, и снова автоматически рендерится?