此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>{" "}
<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">
<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;
1条答案
按热度按时间ar7v8xwq1#
处理这种类型的跨UI视图的数据更改通知的一种简洁方法是使用事件总线模式。
在AddCategory模块中,在将POST提取到后端后引发如下事件:
在ProductsTreeView模块中,按如下方式使用事件:
您可以在application startup创建事件总线示例(例如,在index.js中),然后将其作为
props
传递给所有需要它的视图。为了只通知直接父节点,我使用了props中提供的一个简单的回调函数。当需要更多的视图遍历时,我倾向于使用事件。下面是我使用这个设计模式的一些示例UI: