我使用一个对象数组来递归地绘制一棵树.我能够调出具有所有展开折叠相关功能的树.我在某种程度上实现了在整个树中进行搜索的搜索功能.如果匹配了叶子,我会显示展开到该 node 的所有 node .当搜索中间 node 或根 node 时,我应该显示它处于折叠状态,然后可以展开它

但是,当我搜索第一级或中间父 node 时,搜索结果是正确的,但当我试图打开它的子 node 时,结果是空的.此外,当我搜索任何父母时,图标似乎都会展开.

例如,当我搜索Category1时,树显示Category1带有展开的图标(它本应处于折叠状态),而当我试图展开它的空图标时. 当我搜索Applications时,树显示了从类别1到应用程序的右侧层次 struct ,但应用程序将处于展开状态,并且那里也没有显示任何子项.

有人能告诉我我做错了什么吗?

Sandbox :https://codesandbox.io/s/searching-c1m50i?file=/src/DrawnTree.jsx

到目前为止我try 过的代码

import React, { useState } from "react";
import "./styles.css";
import { Node } from "./Node";

const DrawnTree = ({ treeData, currentActive, setCurrentActive }) => {
  const [search, setSearch] = useState("");

  const containsNodesWithTerm = (nodes, searchTerm) => {
    const ids = [];
    const _traverse = (nodes, searchTerm) => {
      nodes.forEach((node) => {
        if (node.name.toUpperCase().includes(searchTerm.toUpperCase())) {
          ids.push(node.key);
        }
        if (node.nodes.length) {
          _traverse(node.nodes, searchTerm);
        }
      });
    };
    _traverse(nodes, searchTerm);
    return ids.length;
  };

  const filterNodes = (nodes, searchTerm = "") => {
    const _filter = (nodes, searchTerm) => {
      nodes.forEach((node) => {
        if (
          node.name.toUpperCase().includes(searchTerm.toUpperCase()) ||
          containsNodesWithTerm(node.nodes, searchTerm)
        ) {
          node.visible = true;
          node.opened = true;
        } else {
          node.visible = false;
        }
        if (!searchTerm) {
          node.opened = false;
        }
        if (node.nodes.length) {
          _filter(node.nodes, searchTerm);
        }
      });
    };
    _filter(nodes, searchTerm);
    return nodes;
  };

  const filteredTree = filterNodes(treeData, search);

  return (
    <div>
      <input type="text" onChange={(e) => setSearch(e.target.value)} />
      <div className="tree-list-section">
        <div className="left-holder-bottom-list-section">
          {filteredTree.map((node) => (
            <Node
              key={node.key}
              node={node}
              level={0}
              currentActive={currentActive}
              setCurrentActive={setCurrentActive}
            />
          ))}
        </div>
      </div>
    </div>
  );
};

export default DrawnTree;

import React, { useEffect, useState } from "react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";

export const Node = ({ node, level, currentActive, setCurrentActive }) => {
  const { name, key, nodes, visible, opened } = node;

  const [isOpen, setIsOpen] = useState(opened);
  const hasChildren = !!node?.nodes?.length;
  const nodeType = level === 0 ? "node" : !hasChildren ? "leaf" : "group";
  const activeClassName = currentActive === name ? "active" : "";

  useEffect(() => {
    setIsOpen(opened);
  }, [opened]);

  if (!visible) {
    return null;
  }

  return (
    <>
      <div
        className={`list-row level-${level} ${nodeType} ${activeClassName}`}
        onClick={() => {
          setIsOpen((open) => !open);
          if (!hasChildren) {
            setCurrentActive((prevName) => (!(prevName === name) ? name : ""));
          }
        }}
        key={key}
      >
        <div
          className="list-item-holder"
          style={{ paddingLeft: `${level === 0 ? "16" : 40 * level}px` }}
        >
          {hasChildren && (
            <div className="list-item-expander-holder">
              <span
                className={`expand-collapse-icon ${
                  isOpen ? "collapse" : "expand"
                }`}
              >
                <span className="expand-icon">
                  <FontAwesomeIcon icon="caret-down" />
                </span>
                <span className="collapse-icon">
                  <FontAwesomeIcon icon="caret-right" />
                </span>
              </span>
            </div>
          )}
          <div className="list-item-details-holder">{name}</div>
        </div>
      </div>
      {isOpen && hasChildren && (
        <div className="list-row-children">
          {nodes.map((node) => (
            <Node
              key={node.key}
              node={node}
              level={level + 1}
              currentActive={currentActive}
              setCurrentActive={setCurrentActive}
            />
          ))}
        </div>
      )}
    </>
  );
};
 

推荐答案

这是因为当你找到关键字时,你没有让子元素们知道他们的父母匹配关键字的状态.我将您的递归函数修改为:

const filterNodes = (nodes, searchTerm = "") => {
    const _filter = (nodes, searchTerm, isFound) => {
      nodes.forEach((node) => {
        let currentIsFound = false;
        if (node.name.toUpperCase().includes(searchTerm.toUpperCase())) {
          node.visible = true;
          node.opened = false;
          currentIsFound = true;
        } else if (containsNodesWithTerm(node.nodes, searchTerm)
        ) {
          node.visible = true;
          node.opened = true;
        } else if (isFound) {
          node.visible = true;
          node.opened = false;
        } else {
          node.visible = false;
        }
        if (!searchTerm) {
          node.opened = false;
        }
        if (node.nodes.length) {
          _filter(node.nodes, searchTerm, currentIsFound || isFound);
        }
      });
    };
    _filter(nodes, searchTerm, false);
    return nodes;
  };

叉形Sandbox :

Edit searching-forked-hs2r8m

Javascript相关问答推荐

如何保持子画布元素的1:1宽高比?

Redux工具包查询(RTKQ)端点无效并重新验证多次触发

成功完成Reducers后不更新状态

setcallback是什么时候放到macrotask队列上的?

为什么我的自定义元素没有被垃圾回收?

Angular 形式,从DOM中删除不会删除指定索引处的内容,但会删除最后一项

如何在我的Next.js项目中.blob()我的图像文件?

如何在DYGRAPS中更改鼠标事件和键盘输入

如何在FastAPI中为通过file:/URL加载的本地HTML文件启用CORS?

<;img>;标记无法呈现图像

FileReader()不能处理Firefox和GiB文件

JWT Cookie安全性

MongoDB通过数字或字符串过滤列表

如何在独立的Angular 应用程序中添加Lucide-Angel?

为什么我不能使用其同级元素调用和更新子元素?

为什么我的Navbar.css没有显示在本地主机页面上?

Refine.dev从不同的表取多条记录

在不使用AJAX的情况下将JavaScript数组值传递给Laravel控制器?

使用JAVASCRIPT-使用If和Else If多次判断条件-使用JAVASRIPT对象及其属性

已在EventListener中更新/更改异步的React.js状态对象,但不会导致组件重新呈现