reactjs React:当表单在另一个组件中提交时,如何重新呈现树组件?

anhgbhbe  于 2023-01-30  发布在  React
关注(0)|答案(1)|浏览(122)

bounty已结束。此问题的答案可获得+100的信誉奖励。奖励宽限期将在23小时后结束。TestUser正在寻找来自信誉良好来源的答案

我有一个关于刷新react中的组件的问题。页面显示两个组件。左侧面板显示树组件。树节点单击将转到页面右侧的相应页面。问题:右侧组件包括添加/编辑/删除节点等。每个节点右侧显示不同的组件,例如:添加类别表单提交添加节点不刷新页面,不刷新右边组件如何在表单提交时刷新树组件不刷新页面请看我在沙箱中的工作树代码:https://codesandbox.io/s/shy-snow-nfce4i在我的沙箱节点路由不工作。
此外,单击“Service”节点后,页面外观如下所示:https://i.stack.imgur.com/QeYB6.gif
请在沙箱中找到我的完整代码:https://codesandbox.io/s/magical-pond-foqvpq
请在我的代码帮助如何刷新其他组件中的表单提交树,而不刷新整个页面。

App.js
import React, { Component, createRef } from "react";
import { Link } from "react-router-dom";
import ProductsTree from "./ProductsTreeView";
import AddCategory from "./Add_Category";
const initialState = {
  currentNode: {},
  data: ""
};

export class App extends Component {
  constructor(props) {
    super(props);
    this.state = initialState;
    this.productsTree = createRef();
    this.setCurrentNode = this.setCurrentNode.bind(this);
  }

  setCurrentNode(node) {
    this.setState({ currentNode: node });
  }
  ExpandAll() {
    this.productsTree.current.treeView.current.ExpandAll();
  }

  CollapseAll() {
    this.productsTree.current.treeView.current.CollapseAll();
  }

  render() {
    return (
      <div width="100%">
        <div
          width="50%"
          style={{
            height: "200px",
            border: "1px solid rgba(0, 0, 0, 1)",
            display: "inline-block"
          }}
        >
          <Link
            onClick={() => {
              this.ExpandAll();
            }}
          >
            <b>Expand All</b>
          </Link>{" "}
          &nbsp;&nbsp;
          <Link
            onClick={() => {
              this.CollapseAll();
            }}
          >
            <b>Minimize All</b>
          </Link>
          <ProductsTree
            ref={this.productsTree}
            setCurrentNode={this.setCurrentNode}
          />
        </div>
        <div
          width="50%"
          style={{
            border: "1px solid rgba(0, 0, 0, 1)",
            display: "inline-block"
          }}
        >

        <Route path="/Add_Category"> 
          <AddCategory key_id={this.state.currentNode.key_id} />
        </Route> 
        </div>
      </div>
    );
  }
}

export default App;

import React, { Component, createRef } from "react";
import XMLParser from "react-xml-parser";
import { Link } from "react-router-dom";
import styled from "styled-components";
import plus from "./plus.gif";
import minus from "./minus.gif";
import paper from "./paper.gif";

const tree = `
<?xml version="1.0" standalone="yes"?>

<tree>

       <entity id="e11" key-id="1" link-page-name="Add_Category">
              <description>Service</description>
              <image>plus.gif</image>
              <imageOpen>minus.gif</imageOpen>
              <entity id="e248" key-id="48" link-page-name="Edit_Category">
                     <description>A_test1_test1</description>
                     <image>plus.gif</image>
                     <imageOpen>minus.gif</imageOpen>
                     <entity id="e3717" key-id="717" link-page-name="Edit_Product">
                           <description>A_SubItem1</description>
                           <image>plus.gif</image>                      <imageNode>de.gif</imageNode>
                           <imageOpen>minus.gif</imageOpen>
                           <entity id="e45546" key-id="5546" link-page-name="Edit_ProdTemplate">
                                  <description>A_Test_Template</description>
                                  <image>paper.gif</image>
                                  <imageOpen>paper.gif</imageOpen>
                           </entity>
                     </entity>
              </entity>
              <entity id="e264" key-id="64" link-page-name="Add_Product">
              <description>111AAAA</description>        
              <image>plus.gif</image>       
              <imageOpen>minus.gif</imageOpen>      
            </entity>   
            <entity id="e256" key-id="56" link-page-name="Add_Product">       
              <description>11323789</description>  
              <image>plus.gif</image>     
              <imageOpen>minus.gif</imageOpen>     
            </entity>
              <entity id="e247" key-id="47" link-page-name="Edit_Category">
                     <description>A_test6</description>
                     <image>plus.gif</image>
                     <imageOpen>minus.gif</imageOpen>

                     <entity id="e3716" key-id="716" link-page-name="Edit_Product">
                           <description>A_Item</description>
                           <image>plus.gif</image>
                           <imageOpen>minus.gif</imageOpen>
                           <entity id="e45545" key-id="5545" link-page-name="Edit_ProdTemplate">
                                  <description>temp1</description>
                                  <image>paper.gif</image>
                                  <imageOpen>paper.gif</imageOpen>

                           </entity>

                     </entity>

              </entity>

 

       </entity>

       <entity id="e12" key-id="2" link-page-name="Add_Category">

              <description>Sales</description>

              <image>plus.gif</image>

              <imageOpen>minus.gif</imageOpen>

              <entity id="e230" key-id="30" link-page-name="Edit_Category">

                     <description>Gift Cards</description>

                     <image>plus.gif</image>
                  <imageOpen>minus.gif</imageOpen>

                     <entity id="e3421" key-id="421" link-page-name="Edit_Product">

                           <description>Sample Card</description>

                           <image>plus.gif</image>
                           <imageOpen>minus.gif</imageOpen>

                           <entity id="e43308" key-id="3308" link-page-name="Edit_ProdTemplate">

                                  <description>greeting temp</description>

                                  <image>paper.gif</image>

                                  <imageOpen>paper.gif</imageOpen>

                           </entity>

                     </entity>

                     <entity id="e3422" key-id="422" link-page-name="Edit_Product">

                           <description>De Card</description>

                           <image>plus.gif</image>
                           <imageOpen>minus.gif</imageOpen>

                           <entity id="e43309" key-id="3309" link-page-name="Edit_ProdTemplate">

                                  <description>NS Temp</description>

                                  <image>paper.gif</image>

                                  <imageOpen>paper.gif</imageOpen>

                           </entity>

                     </entity>

 

              </entity>

              <entity id="e215" key-id="15" link-page-name="Edit_Category">

                     <description>Chck</description>

                     <image>plus.gif</image>

                     <imageOpen>minus.gif</imageOpen>

                     <entity id="e3671" key-id="671" link-page-name="Edit_Product">

                           <description>Add item</description>

                           <image>plus.gif</image>
                           <imageOpen>minus.gif</imageOpen>

                           <entity id="e45438" key-id="5438" link-page-name="Edit_ProdTemplate">

                                  <description>Ahhhh</description>

                                  <image>paper.gif</image>

                                  <imageOpen>paper.gif</imageOpen>

                           </entity>

                     </entity>

                     <entity id="e3450" key-id="450" link-page-name="Edit_Product">
                           <description>Advtttt</description>
                           <image>plus.gif</image>
                           <imageOpen>minus.gif</imageOpen>
                           <entity id="e43577" key-id="3577" link-page-name="Edit_ProdTemplate">
                                  <description>gggggg</description>
                                  <image>paper.gif</image>
                                  <imageOpen>paper.gif</imageOpen>
                           </entity>
                     </entity>
              </entity>
       </entity>

</tree>
`;

const StyledLI = styled.li`
  list-style-type: none;

  ::before {
    content: "";

    display: inline-flex;

    width: 16px;

    height: 16px;

    ${({ expanded, isPaper }) =>
      `background: url(${
        isPaper === paper ? paper : expanded ? minus : plus
      })};`};
  }
`;

class ProductsTreeView extends Component {
  constructor(props) {
    super(props);

    this.treeView = createRef();
  }

  render() {
    return (
      <div id="TreeView">
        <TreeView
          ref={this.treeView}
          setCurrentNode={this.props.setCurrentNode}
        />
      </div>
    );
  }
}

class Node {
  description = "n/a";

  id = -1;

  key_id = -1;

  linkpagename = "";

  icon = "";

  isActive = false;

  nodes = [];

  constructor(description, id, key_id, icon, linkpagename) {
    this.description = description;

    this.id = id;

    this.key_id = key_id;

    this.icon = icon;

    this.linkpagename = linkpagename;
  }

  static nodesFromXml(xml) {
    const map = (entity, nodes) => {
      const id = entity.attributes["id"];

      const key_id = entity.attributes["key-id"];

      const descriptionText =
        entity.children[
          entity.children.findIndex((child) => child.name === "description")
        ].value;

      const entities = entity.children.filter(
        (child) => child.name === "entity"
      );

      var linkPageName = entity.attributes["link-page-name"];

      linkPageName = linkPageName.replace(".aspx", "");

      const icon =
        entity.children[
          entity.children.findIndex((child) => child.name === "imageOpen")
        ].value;

      const node = new Node(descriptionText, id, key_id, icon, linkPageName);

      nodes.push(node);

      entities.forEach((entity) => map(entity, node.nodes));
    };

    const parsedData = new XMLParser().parseFromString(xml);

    const entities = parsedData.children.filter(
      (child) => child.name === "entity"
    );

    const nodes = [];

    entities.forEach((entity) => map(entity, nodes));

    return nodes;
  }
}

class TreeView extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      //  nodes: []
      nodes: Node.nodesFromXml(tree)
    };

    this.toggleNode = this.toggleNode.bind(this);

    this.CollapseAll = this.CollapseAll.bind(this);

    this.ExpandAll = this.ExpandAll.bind(this);
  }

  componentDidMount() {
    // axios
    //   .get(tree, { "Content-Type": "application/xml; charset=utf-8" })
    //   .then((response) =>{
    //     console.log({ response })
    //     // this.setState({ nodes: Node.nodesFromXml(response.data) })
    //   })
    //   .catch((error) => console.error("Error:", error));
  }

  render() {
    const nodes = this.state.nodes;

    return (
      <div>
        <table width="100%">
          <tr>
            <td width="50%" align="left">
              &nbsp;&nbsp;<b>Products</b>
            </td>
            <td width="50%" style={{ textAlign: "right" }}></td>
          </tr>
        </table>

        <ul>
          {nodes.map((node) => (
            <TreeNode
              id={node.id}
              key={node.key_id}
              node={node}
              onToggle={this.toggleNode}
              setCurrentNode={this.props.setCurrentNode}
            />
          ))}
        </ul>
      </div>
    );
  }

  toggleNode(node) {
    const { nodes } = this.state;

    this.props.setCurrentNode(node);

    function _toggleNode(currentNode, node, isActive) {
      if (isActive !== undefined) {
        currentNode.isActive = isActive;
      } else if (currentNode.id === node.id) {
        if (currentNode.key_id === node.key_id) {
          currentNode.isActive = !currentNode.isActive;
        }
      }

      currentNode.nodes.forEach((childNode) =>
        _toggleNode(childNode, node, currentNode.isActive ? undefined : false)
      );
    }

    nodes.forEach((currentNode) => _toggleNode(currentNode, node));

    this.setState((state) => (state.nodes = nodes));
  }

  CollapseAll() {
    const { nodes } = this.state;

    function _collapseAll(currentNode) {
      currentNode.isActive = false;

      currentNode.nodes.forEach((childNode) => _collapseAll(childNode));
    }

    nodes.forEach((currentNode) => _collapseAll(currentNode));

    this.setState((state) => (state.nodes = nodes));
  }

  ExpandAll() {
    const { nodes } = this.state;

    function _expandAll(currentNode) {
      currentNode.isActive = true;

      currentNode.nodes.forEach((childNode) => _expandAll(childNode));
    }

    nodes.forEach((currentNode) => _expandAll(currentNode));

    this.setState((state) => (state.nodes = nodes));
  }
}

class TreeNode extends React.Component {
  render() {
    const { node, onToggle } = this.props;

    const activeChildren =
      node.isActive && node.nodes.length ? (
        <ul style={{ paddingLeft: "1.8rem" }}>
          {node.nodes.map((node) => (
            <TreeNode
              id={node.id}
              key={node.key_id}
              node={node}
              onToggle={onToggle}
            />
          ))}
        </ul>
      ) : null;

    return (
      <Link
        to={node.linkpagename}
        key={node.key_id}
        onClick={(event) => {
          event.stopPropagation();

          onToggle(node);
        }}
        style={{ textDecoration: "none", color: "#000000" }}
      >
        <StyledLI
          id={node.id}
          expanded={node.isActive}
          isPaper={node.icon}
          isLeaf={!node.nodes.length}
        >
          {node.description}

          {/*- {node.key_id} - {node.linkpagename}*/}

          {activeChildren}
        </StyledLI>
      </Link>
    );
  }
}

export default ProductsTreeView;

import React from "react";
export class Add_Category extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      ID: "",
      CategoryName: "",
      ValidationStatus: ""
    };
    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }
  handleChange(event) {
    this.setState({ CategoryName: event.target.value });
  }
  handleSubmit(event) {
    event.preventDefault();
    const ID = this.props.key_id;
    const CategoryName = this.state.CategoryName;
    const data = { ID, CategoryName };
    fetch("url", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      credentials: "include",
      body: JSON.stringify(data)
    })
      .then((response) => response.json())
      .then((data) => {
        this.setState({
          ValidationStatus: data
        });
      })
      .catch((error) => console.error("Error:", error));
  }
  render() {
    return (
      <div>
        {" "}
        Add Form
        <form onSubmit={this.handleSubmit}>
          <br />
          Name:{" "}
          <input
            type="text"
            value={this.state.CategoryName}
            onChange={this.handleChange}
            size="50"
            maxLength="50"
          />
          <br />
          <br />
          <button type="Submit" className="SaveButton">
            Submit
          </button>
        </form>
      </div>
    );
  }
}

export default Add_Category;
ar7v8xwq

ar7v8xwq1#

处理这种类型的跨UI视图的数据更改通知的一种简洁方法是使用事件总线模式。

    • 添加事件总线**
npm install js-event-bus --save
    • 定义事件类型**
export class ItemAddedEvent {
    public readonly itemName: string
}
    • 引发事件**

在AddCategory模块中,在将POST提取到后端后引发如下事件:

props.eventBus.emit('ItemAdded', null, new ItemAddedEvent('somename'));
    • 尽情享受活动**

在ProductsTreeView模块中,按如下方式使用事件:

useEffect(() => {
    startup();
    return () => cleanup();
}, []);

function startup(): {
    props.eventBus.on('ItemAdded', onItemAdded);
}

function cleanup() {
    props.eventBus.detach('ItemAdded', onItemAdded);
}

function onItemAdded(event: ItemAddedEvent) {
    // update the tree view state here
}
    • 事件总线创建**

您可以在application startup创建事件总线示例(例如,在index.js中),然后将其作为props传递给所有需要它的视图。

    • 摘要**

为了只通知直接父节点,我使用了props中提供的一个简单的回调函数。当需要更多的视图遍历时,我倾向于使用事件。下面是我使用这个设计模式的一些示例UI:

相关问题