Вопрос или проблема
Здравствуйте, я реализую TreeView с помощью React.
Одно из требований – снять выделение с родителя, если хотя бы один из детей не выделен. Я только начинаю работать с React и не уверен, как это сделать.
Пожалуйста, посмотрите на всю мою реализацию здесь: https://playcode.io/2019412
В TreeContext.jsx следующая функция:
const toggleNode = (nodes, id, parent, expanded, checked) => {
return nodes.map((node) => {
// если нажали на родителя
if (node.id === id && node.children?.length > 0) {
// Установить или снять выделение со всех детей
const children = node.children;
const newChildren = children.map((child) => {
return { ...child, isChecked: checked };
});
return {
...node,
isExpanded: expanded,
isChecked: checked,
children: newChildren,
};
}
// если нажали на ребенка
if (node.id === id && node.children === null) {
// Установить индивидуальное выделение
return { ...node, isExpanded: expanded, isChecked: checked };
}
if (node.id === parent.toString()) {
const allChildrenChecked = node.children.every(
(child) => child.isChecked
);
}
if (node.children?.length > 0) {
return {
...node,
children: toggleNode(node.children, id, parent, expanded, checked),
};
}
return node;
});
};
Это выполняет задачу.
Тем не менее, как я могу реализовать:
- Снять выделение с чекбокса родителя, если любой из детей не выделен?
- Существует ли более простой способ представить объект в data.js?
Ответ или решение
Чтобы реализовать функциональность, при которой родительский элемент будет автоматически снят с выбора, если хотя бы один из дочерних элементов не выбран, необходимо внести изменения в вашу функцию toggleNode
. Ваша текущая реализация проверяет, все ли дочерние элементы выбраны, но не производит действий по изменению состояния родителя.
Вот как можно изменить вашу функцию:
const toggleNode = (nodes, id, parent, expanded, checked) => {
return nodes.map((node) => {
// Если кликнули по родительскому элементу
if (node.id === id && node.children?.length > 0) {
// Проверить или снять выбор со всех дочерних элементов
const newChildren = node.children.map((child) => {
return { ...child, isChecked: checked };
});
return {
...node,
isExpanded: expanded,
isChecked: checked,
children: newChildren,
};
}
// Если кликнули по дочернему элементу
if (node.id === id && node.children === null) {
return { ...node, isExpanded: expanded, isChecked: checked };
}
// Если изменяется любой дочерний элемент, необходимо проверить, нужно ли снимать выбор с родителя
if (node.children?.length > 0) {
const updatedChildren = toggleNode(node.children, id, node.id, expanded, checked);
const allChildrenChecked = updatedChildren.every(child => child.isChecked);
return {
...node,
children: updatedChildren,
isChecked: allChildrenChecked ? true : false // Установка состояния выбора родителя
};
}
return node;
});
};
Пояснение к изменению:
-
Работа с дочерними элементами: При изменении состояния дочернего элемента мы обновляем их состояние с помощью рекурсивного вызова
toggleNode
. После обновления дочерних элементов проверяем, все ли они выбраны. Если да, устанавливаемisChecked
у родительского элемента вtrue
, в противном случае — вfalse
. -
Установка состояния родителя: Мы добавили логику для изменения состояния
isChecked
родителя, исходя из состояния его дочерних элементов.
Что касается второго вопроса:
Для упрощения представления объектов в data.js
, вы можете рассмотреть использование более простой структуры данных. Например, вы можете хранить только необходимые поля и использовать вложенные объекты для представления иерархии:
const treeData = [
{
id: '1',
name: 'Родитель 1',
isChecked: false,
children: [
{ id: '1-1', name: 'Дочерний 1', isChecked: false },
{ id: '1-2', name: 'Дочерний 2', isChecked: false },
],
},
{
id: '2',
name: 'Родитель 2',
isChecked: false,
children: [
{ id: '2-1', name: 'Дочерний 1', isChecked: false },
],
},
];
Заключение
Теперь ваша реализация должна корректно обрабатывать снятие выбора с родительского элемента, если хотя бы один дочерний элемент снят с выбора. Также предложенная структура данных упрощает понимание и модификацию дерева. Если у вас есть дополнительные вопросы, не стесняйтесь спрашивать!