React TreeView: отменить выбор родителя, если один из детей не выбран

Вопрос или проблема

Здравствуйте, я реализую 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;
  });
};

Это выполняет задачу.

Тем не менее, как я могу реализовать:

  1. Снять выделение с чекбокса родителя, если любой из детей не выделен?
  2. Существует ли более простой способ представить объект в 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;
  });
};

Пояснение к изменению:

  1. Работа с дочерними элементами: При изменении состояния дочернего элемента мы обновляем их состояние с помощью рекурсивного вызова toggleNode. После обновления дочерних элементов проверяем, все ли они выбраны. Если да, устанавливаем isChecked у родительского элемента в true, в противном случае — в false.

  2. Установка состояния родителя: Мы добавили логику для изменения состояния 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 },
    ],
  },
];

Заключение

Теперь ваша реализация должна корректно обрабатывать снятие выбора с родительского элемента, если хотя бы один дочерний элемент снят с выбора. Также предложенная структура данных упрощает понимание и модификацию дерева. Если у вас есть дополнительные вопросы, не стесняйтесь спрашивать!

Оцените материал
Добавить комментарий

Капча загружается...