React 正在将旧状态发送给它的父级
React is sending old state to its parent
当我将子组件的状态发送到其父组件时,React 将旧状态发送到父组件。
我想在每次点击 ListItem 时发送更新的状态,它正在正常工作并调用函数 handleItemClick。
但是当我打电话时 sendStateToParent
。它正在传递它的旧状态。假设我点击了 ITEM1
,它正在发送空数组 []
。接下来我点击了 ITEM2
,它正在发送数组 [ITEM1]
。
这里我实际上是在创建一个 multiselect 下拉菜单。它也可以根据它获得的道具作为单个 select。
import React from 'react';
import ListItemComponent from './ListItem.jsx';
import DropDownButtonComponent from './DropDownButton.jsx';
import DropDownStyle from '../../../../css/sass/drop-down.scss';
module.exports = React.createClass({
handleClick: function () {
this.setState({open: !this.state.open});
},
getInitialState: function () {
return {
open: false,
//listItems: this.props.listItems,
selectedItems:[],
title: this.props.dropdownTitle
}
},
handleItemClick: function (item) {
var selectedItems = [];
if(this.props.multiple == true){
selectedItems = this.state.selectedItems;
if(selectedItems.indexOf(item)==-1){
selectedItems.push(item);
}else{
selectedItems.splice(selectedItems.indexOf(item),1)
}
this.setState({
title: this.state.selectedItems.length+" selected",
selectedItems: selectedItems
});
} else{
selectedItems = [];
selectedItems.push(item);
this.setState({
title: item,
selectedItems: selectedItems,
open: false
});
}
this.sendStateToParent();
},
sendStateToParent: function(){
this.props.ifListChanged(this);
},
handleTextChange: function (event) {
var filteredItems = [];
this.props.listItems.map(function(item){
if(item.toLowerCase().search(event.target.value.toLowerCase()) != -1){
filteredItems.push(item);
}
},this);
this.setState({
listItems: filteredItems
});
},
clearSelected: function(){
this.setState({
title: this.props.dropdownTitle,
selectedItems: [],
});
},
render: function () {
var index = 0;
var list=[];
if (this.state.listItems != undefined) {
list = this.state.listItems.map(function (item) {
return (
<ListItemComponent
key={index++}
item={item}
whenItemClicked={this.handleItemClick}
className={this.state.selectedItems.indexOf(item) != -1 ? "active" : ""}
/>);
}.bind(this));
} else {
list = this.props.listItems.map(function (item) {
return (
<ListItemComponent
key={index++}
item={item}
whenItemClicked={this.handleItemClick}
className={this.state.selectedItems.indexOf(item) != -1 ? "active" : ""}
/>);
}.bind(this));
}
return <div className="btn-group bootstrap-select form-control">
<DropDownButtonComponent
whenClicked={this.handleClick}
title={this.state.title}
/>
<ul className={"dropdown-menu inner dropdown-menu " + (this.state.open ? "show" : "") }>
{this.props.search? <li><input type="text" style={{margin:"auto", maxWidth:"96%"}} onChange={this.handleTextChange} placeholder="Search"/></li> :""}
<li className="disabled"><a>Select from below list {this.props.multiple ? <i title="clear all" style={{fontSize:"15px"}} onClick={this.clearSelected} className="text-danger fa fa-ban pull-right"></i>: ""}</a></li>
{list}
</ul>
</div>
}
});
提前致谢
之所以parent获取selecteditems的旧值,是因为setState()
是一个异步操作。见 explanation here:
setState()
does not immediately mutate this.state
but creates a
pending state transition. Accessing this.state
after calling this
method can potentially return the existing value.
所以在你的代码中,你发送了更新状态的请求,并且在处理新状态之前,你调用了parent中的方法,以通知parent 关于状态,其中仍然有旧项目。
修复 1: 将当前状态发送到 parent。
要让项目发送当前状态,您可以使用 setState()
提供的回调。在反应页面中也有解释:
The second (optional) parameter is a callback function that will be
executed once setState
is completed and the component is re-rendered.
这确保仅在 setState()
完成后才调用 parent。像这样:
this.setState(
{
title: item,
selectedItems: selectedItems,
open: false
},
this.sendStateToParent
);
修复 2(可选但推荐):将 selecteditems 状态移动到 parent.
如果您的 parent 需要了解所选项目,我建议您不要将它们放入列表状态。您的组件对所选项目所做的唯一事情就是在每次单击项目时将它们发送到 parent。
相反,最好是:
- 将 selectedItems 置于 parent
的状态
- 从 parent,将 selectedItems 作为 props 传递给组件
- 将
handleItemClick
逻辑移动到 parent
- 在 parent 中,更新所选项目的列表,并设置状态(parent)
- 触发列表的 re-render,使用新的 selectedItems 作为道具
当我将子组件的状态发送到其父组件时,React 将旧状态发送到父组件。
我想在每次点击 ListItem 时发送更新的状态,它正在正常工作并调用函数 handleItemClick。
但是当我打电话时 sendStateToParent
。它正在传递它的旧状态。假设我点击了 ITEM1
,它正在发送空数组 []
。接下来我点击了 ITEM2
,它正在发送数组 [ITEM1]
。
这里我实际上是在创建一个 multiselect 下拉菜单。它也可以根据它获得的道具作为单个 select。
import React from 'react';
import ListItemComponent from './ListItem.jsx';
import DropDownButtonComponent from './DropDownButton.jsx';
import DropDownStyle from '../../../../css/sass/drop-down.scss';
module.exports = React.createClass({
handleClick: function () {
this.setState({open: !this.state.open});
},
getInitialState: function () {
return {
open: false,
//listItems: this.props.listItems,
selectedItems:[],
title: this.props.dropdownTitle
}
},
handleItemClick: function (item) {
var selectedItems = [];
if(this.props.multiple == true){
selectedItems = this.state.selectedItems;
if(selectedItems.indexOf(item)==-1){
selectedItems.push(item);
}else{
selectedItems.splice(selectedItems.indexOf(item),1)
}
this.setState({
title: this.state.selectedItems.length+" selected",
selectedItems: selectedItems
});
} else{
selectedItems = [];
selectedItems.push(item);
this.setState({
title: item,
selectedItems: selectedItems,
open: false
});
}
this.sendStateToParent();
},
sendStateToParent: function(){
this.props.ifListChanged(this);
},
handleTextChange: function (event) {
var filteredItems = [];
this.props.listItems.map(function(item){
if(item.toLowerCase().search(event.target.value.toLowerCase()) != -1){
filteredItems.push(item);
}
},this);
this.setState({
listItems: filteredItems
});
},
clearSelected: function(){
this.setState({
title: this.props.dropdownTitle,
selectedItems: [],
});
},
render: function () {
var index = 0;
var list=[];
if (this.state.listItems != undefined) {
list = this.state.listItems.map(function (item) {
return (
<ListItemComponent
key={index++}
item={item}
whenItemClicked={this.handleItemClick}
className={this.state.selectedItems.indexOf(item) != -1 ? "active" : ""}
/>);
}.bind(this));
} else {
list = this.props.listItems.map(function (item) {
return (
<ListItemComponent
key={index++}
item={item}
whenItemClicked={this.handleItemClick}
className={this.state.selectedItems.indexOf(item) != -1 ? "active" : ""}
/>);
}.bind(this));
}
return <div className="btn-group bootstrap-select form-control">
<DropDownButtonComponent
whenClicked={this.handleClick}
title={this.state.title}
/>
<ul className={"dropdown-menu inner dropdown-menu " + (this.state.open ? "show" : "") }>
{this.props.search? <li><input type="text" style={{margin:"auto", maxWidth:"96%"}} onChange={this.handleTextChange} placeholder="Search"/></li> :""}
<li className="disabled"><a>Select from below list {this.props.multiple ? <i title="clear all" style={{fontSize:"15px"}} onClick={this.clearSelected} className="text-danger fa fa-ban pull-right"></i>: ""}</a></li>
{list}
</ul>
</div>
}
});
提前致谢
之所以parent获取selecteditems的旧值,是因为setState()
是一个异步操作。见 explanation here:
setState()
does not immediately mutatethis.state
but creates a pending state transition. Accessingthis.state
after calling this method can potentially return the existing value.
所以在你的代码中,你发送了更新状态的请求,并且在处理新状态之前,你调用了parent中的方法,以通知parent 关于状态,其中仍然有旧项目。
修复 1: 将当前状态发送到 parent。
要让项目发送当前状态,您可以使用 setState()
提供的回调。在反应页面中也有解释:
The second (optional) parameter is a callback function that will be executed once
setState
is completed and the component is re-rendered.
这确保仅在 setState()
完成后才调用 parent。像这样:
this.setState(
{
title: item,
selectedItems: selectedItems,
open: false
},
this.sendStateToParent
);
修复 2(可选但推荐):将 selecteditems 状态移动到 parent.
如果您的 parent 需要了解所选项目,我建议您不要将它们放入列表状态。您的组件对所选项目所做的唯一事情就是在每次单击项目时将它们发送到 parent。
相反,最好是:
- 将 selectedItems 置于 parent 的状态
- 从 parent,将 selectedItems 作为 props 传递给组件
- 将
handleItemClick
逻辑移动到 parent - 在 parent 中,更新所选项目的列表,并设置状态(parent)
- 触发列表的 re-render,使用新的 selectedItems 作为道具