React 组件不会更新设置状态
React component does not update on set state
我正在尝试根据状态内标志的切换有条件地呈现组件。看起来状态正在更新,但组件未呈现。有人能说出这里发生了什么吗? renderTree
函数更新了状态,但是渲染没有被调用。
import React from "react";
import CheckboxTree from "react-checkbox-tree";
import "react-checkbox-tree/lib/react-checkbox-tree.css";
import { build } from "../data";
import { Input, Dropdown } from "semantic-ui-react";
import _ from "lodash";
class Widget extends React.Component {
constructor(props) {
super(props);
this.state = {
nodes: build(),
checked: [],
expanded: [],
isDropdownExpanded: false,
keyword: ""
};
}
onCheck = checked => {
this.setState({ checked }, () => {
console.log(this.state.checked);
});
};
onExpand = expanded => {
this.setState({ expanded }, () => {
console.log(this.state.expanded);
});
};
renderTree = () => {
this.setState(
prevState => {
return {
...prevState,
isDropdownExpanded: !prevState.isDropdownExpanded
};
},
() => {
console.log(this.state);
}
);
};
onSearchInputChange = (event, data, searchedNodes) => {
this.setState(prevState => {
if (prevState.keyword.trim() && !data.value.trim()) {
return {
expanded: [],
keyword: data.value
};
}
return {
expanded: this.getAllValuesFromNodes(searchedNodes, true),
keyword: data.value
};
});
};
shouldComponentUpdate(nextProps, nextState) {
if (this.state.keyword !== nextState.keyword) {
return true;
}
if (!_.isEqual(this.state.checked, nextState.checked)) {
return true;
}
if (_.isEqual(this.state.expanded, nextState.expanded)) {
return false;
}
return true;
}
getAllValuesFromNodes = (nodes, firstLevel) => {
if (firstLevel) {
const values = [];
for (let n of nodes) {
values.push(n.value);
if (n.children) {
values.push(...this.getAllValuesFromNodes(n.children, false));
}
}
return values;
} else {
const values = [];
for (let n of nodes) {
values.push(n.value);
if (n.children) {
values.push(...this.getAllValuesFromNodes(n.children, false));
}
}
return values;
}
};
keywordFilter = (nodes, keyword) => {
let newNodes = [];
for (let n of nodes) {
if (n.children) {
const nextNodes = this.keywordFilter(n.children, keyword);
if (nextNodes.length > 0) {
n.children = nextNodes;
} else if (n.label.toLowerCase().includes(keyword.toLowerCase())) {
n.children = nextNodes.length > 0 ? nextNodes : [];
}
if (
nextNodes.length > 0 ||
n.label.toLowerCase().includes(keyword.toLowerCase())
) {
n.label = this.getHighlightText(n.label, keyword);
newNodes.push(n);
}
} else {
if (n.label.toLowerCase().includes(keyword.toLowerCase())) {
n.label = this.getHighlightText(n.label, keyword);
newNodes.push(n);
}
}
}
return newNodes;
};
getHighlightText = (text, keyword) => {
const startIndex = text.indexOf(keyword);
return startIndex !== -1 ? (
<span>
{text.substring(0, startIndex)}
<span style={{ color: "red" }}>
{text.substring(startIndex, startIndex + keyword.length)}
</span>
{text.substring(startIndex + keyword.length)}
</span>
) : (
<span>{text}</span>
);
};
render() {
const { checked, expanded, nodes, isDropdownExpanded } = this.state;
let searchedNodes = this.state.keyword.trim()
? this.keywordFilter(_.cloneDeep(nodes), this.state.keyword)
: nodes;
return (
<div>
<Dropdown fluid selection options={[]} onClick={this.renderTree} />
{isDropdownExpanded && (
<div>
<Input
style={{ marginBottom: "20px" }}
fluid
icon="search"
placeholder="Search"
iconPosition="left"
onChange={(event, data) => {
this.onSearchInputChange(event, data, searchedNodes);
}}
/>
<CheckboxTree
nodes={searchedNodes}
checked={checked}
expanded={expanded}
onCheck={this.onCheck}
onExpand={this.onExpand}
showNodeIcon={true}
/>
</div>
)}
</div>
);
}
}
export default Widget;
再见,要在 React 中强制重新渲染,您必须使用 shouldComponentUpdate(nextProps, nextState)
函数。类似于:
shouldComponentUpdate(nextProps, nextState) {
return this.state.isDropdownExpanded !== nextState.isDropdownExpanded;
}
当您更改 isDropdownExpanded
值时,将触发 shouldComponentUpdate,如果 return 等于 true,将重新渲染组件。 Here 工作示例(基于您的 codesandbox)。
问题出在您的 shouldComponentUpdate
方法中:
shouldComponentUpdate(nextProps, nextState) {
if (this.state.keyword !== nextState.keyword) {
return true;
}
if (!_.isEqual(this.state.checked, nextState.checked)) {
return true;
}
if (_.isEqual(this.state.expanded, nextState.expanded)) {
return false;
}
return true;
}
由于 renderTree
仅更改 isDropdownExpanded
值,shouldComponentUpdate
始终 returns false
如果 shouldComponenetUpdate
returns true 那么你的组件会重新渲染,否则不会。
在您的代码沙箱中,可以看到每次单击下拉菜单时,shouldComponenetUpdate
returns false 对于此条件
if (_.isEqual(this.state.expanded, nextState.expanded)) {
return false;
}
要么您需要在您的 renderTree 函数中更改此变量的状态,要么您需要将此条件重写为
if (_.isEqual(this.state.isDropdownExpanded, nextState.isDropdownExpanded)) {
return false;
}
我正在尝试根据状态内标志的切换有条件地呈现组件。看起来状态正在更新,但组件未呈现。有人能说出这里发生了什么吗? renderTree
函数更新了状态,但是渲染没有被调用。
import React from "react";
import CheckboxTree from "react-checkbox-tree";
import "react-checkbox-tree/lib/react-checkbox-tree.css";
import { build } from "../data";
import { Input, Dropdown } from "semantic-ui-react";
import _ from "lodash";
class Widget extends React.Component {
constructor(props) {
super(props);
this.state = {
nodes: build(),
checked: [],
expanded: [],
isDropdownExpanded: false,
keyword: ""
};
}
onCheck = checked => {
this.setState({ checked }, () => {
console.log(this.state.checked);
});
};
onExpand = expanded => {
this.setState({ expanded }, () => {
console.log(this.state.expanded);
});
};
renderTree = () => {
this.setState(
prevState => {
return {
...prevState,
isDropdownExpanded: !prevState.isDropdownExpanded
};
},
() => {
console.log(this.state);
}
);
};
onSearchInputChange = (event, data, searchedNodes) => {
this.setState(prevState => {
if (prevState.keyword.trim() && !data.value.trim()) {
return {
expanded: [],
keyword: data.value
};
}
return {
expanded: this.getAllValuesFromNodes(searchedNodes, true),
keyword: data.value
};
});
};
shouldComponentUpdate(nextProps, nextState) {
if (this.state.keyword !== nextState.keyword) {
return true;
}
if (!_.isEqual(this.state.checked, nextState.checked)) {
return true;
}
if (_.isEqual(this.state.expanded, nextState.expanded)) {
return false;
}
return true;
}
getAllValuesFromNodes = (nodes, firstLevel) => {
if (firstLevel) {
const values = [];
for (let n of nodes) {
values.push(n.value);
if (n.children) {
values.push(...this.getAllValuesFromNodes(n.children, false));
}
}
return values;
} else {
const values = [];
for (let n of nodes) {
values.push(n.value);
if (n.children) {
values.push(...this.getAllValuesFromNodes(n.children, false));
}
}
return values;
}
};
keywordFilter = (nodes, keyword) => {
let newNodes = [];
for (let n of nodes) {
if (n.children) {
const nextNodes = this.keywordFilter(n.children, keyword);
if (nextNodes.length > 0) {
n.children = nextNodes;
} else if (n.label.toLowerCase().includes(keyword.toLowerCase())) {
n.children = nextNodes.length > 0 ? nextNodes : [];
}
if (
nextNodes.length > 0 ||
n.label.toLowerCase().includes(keyword.toLowerCase())
) {
n.label = this.getHighlightText(n.label, keyword);
newNodes.push(n);
}
} else {
if (n.label.toLowerCase().includes(keyword.toLowerCase())) {
n.label = this.getHighlightText(n.label, keyword);
newNodes.push(n);
}
}
}
return newNodes;
};
getHighlightText = (text, keyword) => {
const startIndex = text.indexOf(keyword);
return startIndex !== -1 ? (
<span>
{text.substring(0, startIndex)}
<span style={{ color: "red" }}>
{text.substring(startIndex, startIndex + keyword.length)}
</span>
{text.substring(startIndex + keyword.length)}
</span>
) : (
<span>{text}</span>
);
};
render() {
const { checked, expanded, nodes, isDropdownExpanded } = this.state;
let searchedNodes = this.state.keyword.trim()
? this.keywordFilter(_.cloneDeep(nodes), this.state.keyword)
: nodes;
return (
<div>
<Dropdown fluid selection options={[]} onClick={this.renderTree} />
{isDropdownExpanded && (
<div>
<Input
style={{ marginBottom: "20px" }}
fluid
icon="search"
placeholder="Search"
iconPosition="left"
onChange={(event, data) => {
this.onSearchInputChange(event, data, searchedNodes);
}}
/>
<CheckboxTree
nodes={searchedNodes}
checked={checked}
expanded={expanded}
onCheck={this.onCheck}
onExpand={this.onExpand}
showNodeIcon={true}
/>
</div>
)}
</div>
);
}
}
export default Widget;
再见,要在 React 中强制重新渲染,您必须使用 shouldComponentUpdate(nextProps, nextState)
函数。类似于:
shouldComponentUpdate(nextProps, nextState) {
return this.state.isDropdownExpanded !== nextState.isDropdownExpanded;
}
当您更改 isDropdownExpanded
值时,将触发 shouldComponentUpdate,如果 return 等于 true,将重新渲染组件。 Here 工作示例(基于您的 codesandbox)。
问题出在您的 shouldComponentUpdate
方法中:
shouldComponentUpdate(nextProps, nextState) {
if (this.state.keyword !== nextState.keyword) {
return true;
}
if (!_.isEqual(this.state.checked, nextState.checked)) {
return true;
}
if (_.isEqual(this.state.expanded, nextState.expanded)) {
return false;
}
return true;
}
由于 renderTree
仅更改 isDropdownExpanded
值,shouldComponentUpdate
始终 returns false
如果 shouldComponenetUpdate
returns true 那么你的组件会重新渲染,否则不会。
在您的代码沙箱中,可以看到每次单击下拉菜单时,shouldComponenetUpdate
returns false 对于此条件
if (_.isEqual(this.state.expanded, nextState.expanded)) {
return false;
}
要么您需要在您的 renderTree 函数中更改此变量的状态,要么您需要将此条件重写为
if (_.isEqual(this.state.isDropdownExpanded, nextState.isDropdownExpanded)) {
return false;
}