【问题标题】:Child component does not re-render子组件不会重新渲染
【发布时间】:2020-04-18 03:49:13
【问题描述】:

我正在使用两个功能性反应组件,其中我通过道具将单击项目的id(状态)传递给子组件。

问题

id 确实会在子组件中渲染,但是当父组件中的状态更新时,子组件会创建一个新的渲染,而不是更新之前的渲染。所以例如。子组件显示id 1,当单击另一个项目时,子组件同时显示1和新的id

父类:

import ReactDOM from "react-dom";
import React from "react";
import TreeView from "@material-ui/lab/TreeView";
import ExpandMoreIcon from "@material-ui/icons/ExpandMore";
import ChevronRightIcon from "@material-ui/icons/ChevronRight";
import TreeItem from "@material-ui/lab/TreeItem";
import Test from "./test";
const { useState, useCallback } = React;

export default function MyTreeItem(props) {
  const [childNodes, setChildNodes] = useState(null);
  const [expanded, setExpanded] = React.useState([]);
  const [activeItemId, setActiveItemId] = useState();

  function fetchChildNodes(id) {
    return new Promise(resolve => {
      setTimeout(() => {
        resolve({
          children: [
            {
              id: "2",
              name: "Calendar"
            },
            {
              id: "3",
              name: "Settings"
            },
            {
              id: "4",
              name: "Music"
            }
          ]
        });
      }, 1000);
    });
  }

  const handleChange = (event, nodes) => {
    const expandingNodes = nodes.filter(x => !expanded.includes(x));
    setExpanded(nodes);
    if (expandingNodes[0]) {
      const childId = expandingNodes[0];
      fetchChildNodes(childId).then(result =>
        setChildNodes(
          result.children.map(node => <MyTreeItem key={node.id} {...node} />)
        )
      );
    }
  };

  const renderLabel = item => (
    <span
      onClick={event => {
        console.log(item.id);
        setActiveItemId(item.id);
        // if you want after click do expand/collapse comment this two line
        event.stopPropagation();
        event.preventDefault();
      }}
    >
      {item.name}
    </span>
  );

  return (
    <div>
      <TreeView
        defaultCollapseIcon={<ExpandMoreIcon />}
        defaultExpandIcon={<ChevronRightIcon />}
        expanded={expanded}
        onNodeToggle={handleChange}
      >
        {/*The node below should act as the root node for now */}
        <TreeItem nodeId={props.id} label={renderLabel(props)}>
          {childNodes || [<div key="stub" />]}
        </TreeItem>
      </TreeView>
      <Test test={activeItemId} />
    </div>
  );
}
const rootElement = document.getElementById("root");
ReactDOM.render(<MyTreeItem id="1" name="Applications" />, rootElement);


儿童班:

import React, { useState, useEffect } from "react";

export default function Test({ test }) {
  return <h1>{test}</h1>;
}

重现问题: https://codesandbox.io/s/material-demo-5kfbl

【问题讨论】:

  • 我没有看到1 显示在任何地方。它似乎按预期工作。
  • 如果您单击Applications 标签,则会显示1。如果您展开Applicationsnode 并单击Calendar,则会显示2

标签: javascript reactjs


【解决方案1】:

问题来自 [unexpected] recurrency - setChildNodes 将“树根”组件 &lt;MyTreeItem /&gt; 呈现为子项,每个子项都有自己的状态、处理程序等。

您可以通过将 &lt;TreeItem /&gt; 渲染为子级来解决此问题:

    setChildNodes(
      result.children.map(node => <TreeItem nodeId={node.id} label={renderLabel(node)} /> )
    )

工作示例:https://codesandbox.io/s/material-demo-k8mq1

【讨论】:

  • 谢谢,但唯一的问题是当用户展开节点时我正在获取数据。通过此更改,我无法递归扩展节点。
  • 当孩子将被设置/渲染为&lt;TreeItem/&gt; 组件的树结构时,您可以递归地使用它们 - 请参阅材料示例。当您需要嵌套自定义逻辑('subfetching')时,您需要其他组件间通信方法 - f.e.使用上下文能够将事件/值发送到“根”......当然,孩子不应该是相同的类型......或者在“根”之上更好的上下文提供者(相同的组件类型 - 上下文消费者 - 在整个树)。
猜你喜欢
  • 2020-02-13
  • 1970-01-01
  • 2018-08-12
  • 2017-05-31
  • 2021-01-24
  • 2018-09-01
  • 2019-10-05
  • 2023-03-13
  • 1970-01-01
相关资源
最近更新 更多