【问题标题】:TextField lose focusTextField 失去焦点
【发布时间】:2020-05-27 01:30:00
【问题描述】:

我对 TreeView 和 TextField 有疑问。我将这些组件包装在 TreeViewContainer 中。我想通过输入 TextField 来过滤树节点。问题是当我选择节点然后尝试过滤树时。 TextField 失去焦点并选择匹配第一个字符的第一个节点(或有时记录错误)。

Uncaught TypeError: Cannot read property 'parent' of undefined
    at eval (TreeView.js:327)
    at Array.forEach (<anonymous>)
    at setFocusByFirstCharacter (TreeView.js:321)
    at printableCharacter (TreeItem.js:173)
    at handleKeyDown (TreeItem.js:262)
    at HTMLUnknownElement.callCallback (react-dom.development.js:336)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:385)

我想知道是material-ui错误还是我做错了什么?

Working example

【问题讨论】:

  • 请在此处查看我的答案:stackoverflow.com/questions/59063602/…,如果它解决了您的问题,请点赞。
  • @RyanCogswell 它不起作用。我也尝试用普通的 替换 TextField 但问题仍然存在。
  • 你能保存你的沙盒吗?我一看,TreeViewFilter.tsx 是空的。
  • @RyanCogswell 已保存。
  • stopPropagation 需要在onKeyDown 中,而不是onChange 中(例如onKeyDown={e =&gt; e.stopPropagation()})。查看我对您的沙箱的修改:codesandbox.io/s/stoic-chebyshev-93cgi

标签: reactjs material-ui


【解决方案1】:

TreeView 维护focused node id within its state。当您使用不同的结构重新渲染树时(例如删除“根”节点),TreeItems 将被重新安装,而不仅仅是重新渲染。 TreeItem 基于 TreeView 上下文监控 whether or not it should be focused,如果发生变化,它会抓住焦点。当TreeItem 挂载时,该焦点逻辑将执行。通常TreeItem 不会将焦点放在挂载上,但在您的特定场景中,因为TreeView 保持一致并记住最后一个焦点TreeItem,但是所有项目都被重新挂载,它会将焦点带回那个TreeItem

问题的另一个方面是TreeViewremembers its nodes。它@ 987654324@,但看起来您的场景可能会暴露该逻辑中的错误并且键入(一旦焦点回到树项上)试图查找不再存在的节点的父节点(尽管我会需要使用更简单的代码示例进一步深入研究,以确定为什么 nodeMap 不太正确以及这是否真的是一个错误)。

您可以通过在搜索后设置新树时更改TreeView 的键来修复焦点转移和过时的nodeMap 信息。这会导致重新挂载 TreeView,因此它不再记住最后一个焦点节点或旧节点映射。

这是您沙盒中代码的修改版本(我的更改以“由 Ryan 添加”表示):

import React, { useState } from "react";
import TreeViewFilter from "./TreeViewFilter";
import TreeView from "@material-ui/lab/TreeView";
import ExpandMoreIcon from "@material-ui/icons/ExpandMore";
import ChevronRightIcon from "@material-ui/icons/ChevronRight";
import { Folder } from "@material-ui/icons";
import TreeItem from "@material-ui/lab/TreeItem";
import { makeStyles } from "@material-ui/core/styles";
import { amber } from "@material-ui/core/colors";
import _ from "lodash";

const useStyles = makeStyles({
  root: {
    height: 216,
    flexGrow: 1,
    maxWidth: 400
  }
});

enum NodeType {
  FOLDER = 0,
  SCHEMA
}

type TreeNode = {
  name: string;
  type: NodeType;
  parent: string | null;
  childNodes: Array<TreeNode>;
};

export interface ITreeViewProps {
  tree: TreeNode;
  fetchItem?: () => void;
}

export default function TreeViewContainer(props: ITreeViewProps) {
  const classes = useStyles();

  const [searchValue, setSearchValue] = useState("");
  const [tree, setTree] = useState(props.tree);
  // ** Added by Ryan ** New treeKey state
  const [treeKey, setTreeKey] = useState(1);

  function fetchItem(id: string) {
    // inicjacja pobierania schematu/listy alarmów/ raport/ wykresu
    console.log("Fetch  " + id);
  }

  function renderTree(tree: any) {
    if (Array.isArray(tree)) {
      return tree.map(item => buildTree(item));
    } else {
      return buildTree(tree);
    }
  }

  function buildTree(tree: TreeNode) {
    return (function recursive(currentNode: TreeNode) {
      if (currentNode.type === NodeType.FOLDER) {
        return (
          <TreeItem
            key={currentNode.name}
            nodeId={`${currentNode.type}${currentNode.name}`}
            label={
              <div>
                <Folder style={{ color: amber[500] }} />{" "}
                <span>{currentNode.name}</span>
              </div>
            }
          >
            {currentNode.childNodes.map(node => recursive(node))}
          </TreeItem>
        );
      }

      if (currentNode.type === NodeType.SCHEMA) {
        return (
          <TreeItem
            key={currentNode.name}
            nodeId={`${currentNode.type}${currentNode.name}`}
            onClick={() => {
              fetchItem(currentNode.name);
            }}
            label={currentNode.name + ".sh"}
          />
        );
      }

      return null;
    })(tree);
  }

  const searchTree = (tree: TreeNode, searchValue: string, callback: any) => {
    const searchRE = new RegExp(searchValue, "i");
    return (function recurse(currentNode: TreeNode) {
      for (let i = 0, length = currentNode.childNodes.length; i < length; i++) {
        recurse(currentNode.childNodes[i]);
      }
      if (currentNode.name.match(searchRE))
        callback({
          ...currentNode,
          childNodes: currentNode.childNodes.filter(node =>
            node.name.match(searchRE)
          )
        });
    })(tree);
  };

  const doSearch = (text: string) => {
    console.log("-------------------->>>>");

    setSearchValue(text);
    let newTree: any = [];
    searchTree(props.tree, text, function(node: any) {
      console.log(node);
      newTree.push(node);
    });

    console.log("===============");
    console.log(newTree); // usunąć item, którego parent jest tablicy
    function hasParentInCollection(item: TreeNode, index, arr) {
      if (arr.find((el: any) => el.name === item.parent)) return false;
      else return true;
    }
    newTree = newTree.filter(hasParentInCollection);
    //set new tree
    console.log("++++++++++++++++++++++++");
    console.log(newTree);
    setTree(newTree);
    // ** Added by Ryan ** Update treeKey state
    setTreeKey(oldKey => oldKey + 1);
  };

  const treeItems = renderTree(tree);
  return (
    <div>
      <div>
        <TreeViewFilter doSearch={doSearch} value={searchValue} />
      </div>
      <div>
        {/* ** key={treeKey} added by Ryan */}
        <TreeView
          key={treeKey}
          className={classes.root}
          defaultCollapseIcon={<ExpandMoreIcon />}
          defaultExpandIcon={<ChevronRightIcon />}
        >
          {treeItems}
        </TreeView>
      </div>
    </div>
  );
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-24
    • 2022-11-21
    • 2023-01-16
    • 2018-01-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多