我是React / Redux的新手,遇到状态问题。
TrajectContainer.jsx
class TrajectContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
trajects: props.trajects,
onClick: props.onClick
};
}
componentWillReceiveProps(nextProps) {
console.log('componentWillReceiveProps', nextProps);
this.setState(nextProps);
}
render() {
// When the componentWillReceiveProps is not present, the this.state will hold the old state
console.log('rerender', this.state);
return (<div className="col-md-6">
<h2>Trajects</h2>
<button className="btn btn-primary" onClick={this.state.onClick}>Add new Traject</button>
{this.state.trajects.map(traject => <Traject traject={traject} key={traject.name}/>)}
</div>)
}
}
const mapStateToProps = function (store) {
console.log('mapToStateProps', store);
return {
trajects: store.trajects
};
};
const mapDispatchToProps = function (dispatch, ownProps) {
return {
onClick: function () {
dispatch(addTraject());
}
}
};
export default connect(mapStateToProps, mapDispatchToProps)(TrajectContainer);
当reducer返回一个新状态时,该组件将使用新数据重新渲染。
但是:如果我删除了componentWillReceiveProps函数,则render()函数具有旧状态。
我检查了mapStateToProps中收到的数据,这是新的新状态。 所以我不明白为什么我需要componentWillReceiveProps函数才能使渲染函数接收新数据。
我做错了吗?
答案 0 :(得分:39)
componentWillReceiveProps
,只要道具值发生任何变化,就会调用此方法。
在你的情况下为什么需要这个componentWillReceiveProps方法?
因为您将props值存储在状态变量中,并使用它如下:
this.state.KeyName
这就是为什么你需要componentWillReceiveProps
生命周期方法来使用新的道具值来更新状态值,只有组件的道具值会更新,但自动状态不会得到更新。如果您不更新状态,则this.state
将始终拥有初始数据。
componentWillReceiveProps
:
this.props.keyName
现在,反应将始终在渲染方法中使用更新的道具值,如果道具发生任何变化,它将使用新道具重新渲染组件。
根据 DOC :
在安装的组件之前调用componentWillReceiveProps() 收到新的道具。如果您需要更新状态以响应 prop更改(例如,重置它),您可以比较this.props 和nextProps并使用this.setState()执行状态转换 这种方法。
React不使用初始道具调用componentWillReceiveProps 安装。如果组件的某些道具可能会调用此方法 更新
<强>建议:强>
不要将道具值存储在状态中,直接使用this.props
并创建ui组件。
答案 1 :(得分:3)
private Control _parentControl;
protected void Page_Load(object sender, EventArgs e)
{
_parentControl = this; //if using an UpdatePanel use the ID of that instead of 'this'
DataForRepeater();
}
private void DataForRepeater()
{
//just example data to load the repeater
var dt = new DataTable();
dt.Columns.Add("GroupID", typeof(int));
dt.Columns.Add("GroupName", typeof(string));
dt.Columns.Add("GroupDescription", typeof(string));
for (int i = 1; i <= 10; i++)
{
var nr = dt.NewRow();
nr["GroupID"] = i;
nr["GroupName"] = "SomeName" + i.ToString();
nr["GroupDescription"] = "Description of " + i.ToString() + " item";
dt.Rows.Add(nr);
}
DataRepeater.DataSource = dt;
DataRepeater.DataBind();
}
protected void DataRepeater_ItemCommand(object sender, CommandEventArgs e)
{
string command = e.CommandName;
string[] arguments = e.CommandArgument.ToString().Split('|');
switch (command)
{
case ("Edit"):
//edit operation
IdOfItemToEditHiddenField.Value = arguments[0];
NameTextBox.Text = arguments[1];
ScriptManager.RegisterStartupScript(_parentControl, _parentControl.GetType(), "Modal", " DisplayEditModal()", true);
break;
case ("Delete"):
//delete operation
IdOfItemToDeleteHiddenField.Value = arguments[0];
DeleteMessageLabel.Text = arguments[1] + "<br/>" + arguments[2];
ScriptManager.RegisterStartupScript(_parentControl, _parentControl.GetType(), "Modal", " DisplayDeleteModal()", true);
break;
default:
throw new InvalidOperationException();
}
}
protected void btnSaveEdit_Click(object sender, EventArgs e)
{
int idToSave = int.Parse(IdOfItemToEditHiddenField.Value);
string newName = NameTextBox.Text;
//do something to save it
ScriptManager.RegisterStartupScript(_parentControl, _parentControl.GetType(), "Msg", string.Format("alert('Save item {0}: {1}');", idToSave, newName), true);
}
protected void btnDelete_Click(object sender, EventArgs e)
{
int idToDelete = int.Parse(IdOfItemToDeleteHiddenField.Value);
//do something to delete it
ScriptManager.RegisterStartupScript(_parentControl, _parentControl.GetType(), "Msg", string.Format("alert('Delete item {0}');", idToDelete), true);
}
而不是componentWillReceiveProps
还会看到article from gaearon重写弹性组件
这里有两个潜在的问题
避免状态意味着您不再需要构造函数或生命周期方法。因此,您的组件可以编写为无状态功能组件,以这种方式编写组件会带来性能优势。
下面是一个代码片段,它从组件中删除状态并依赖于从redux商店返回的状态
componentDidUpdate()
&#13;
答案 2 :(得分:2)
我有类似问题添加withRouter()
,如下所示:
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(TrajectContainer));
答案 3 :(得分:1)
您的实现问题是您正在将Redux存储状态(从道具中提交)复制到React状态(this.state)
在您的示例中,如果store.trajects
已更新,则this.props.traject
将更新,只有在您的渲染方法中使用this.props.traject
时才会触发渲染(不是这种情况)。
由于您在渲染方法中使用状态而不是prop,因此必须使用this.setState
手动更改组件的状态以触发渲染。
这不是一个好模式:我建议不要使用状态,直接使用这样的道具:
class TrajectContainer extends React.Component {
render() {
return (<div className="col-md-6">
<h2>Trajects</h2>
<button className="btn btn-primary" onClick={this.props.onClick}>Add new Traject</button>
{this.props.trajects.map(traject => <Traject traject={traject} key={traject.name}/>)}
</div>)
}
}
const mapStateToProps = function (store) {
console.log('mapToStateProps', store);
return {
trajects: store.trajects
};
};
const mapDispatchToProps = function (dispatch, ownProps) {
return {
onClick: function () {
dispatch(addTraject());
}
}
};
export default connect(mapStateToProps, mapDispatchToProps)(TrajectContainer)
答案 4 :(得分:0)
在您的情况下,您将需要componentWillReceiveProps
,并且您必须在收到新道具时更新状态。因为
在您的构造函数中,您已将状态声明如下。正如您所看到的,您使用传入的道具构建了state
。(这就是您需要componentWillReceiveProps
以及在那里更新它的逻辑)
this.state = {
trajects: props.trajects,
onClick: props.onClick
};
所以当你的道具,改变componentWillReceiveProps
是被调用的函数。构造函数不会被调用。因此,您必须再次设置状态,以便更改进入组件的状态。
但您的逻辑应如下所示。使用条件,以便在多次调用时可以防止重复的状态更新。
componentWillReceiveProps(nextProps) {
console.log('componentWillReceiveProps', nextProps);
if (this.props !== nextProps) {
this.setState(nextProps);
}
}
只有在您要修改其内容时,才应将道具存储到状态。但在你的情况下,我看到没有修改。因此,您可以直接直接使用this.props.trajects
,而不是将其存储到状态中然后使用它。这样你就可以摆脱componentWillReceiveProps
所以你的渲染函数将使用类似下面的内容
{this.props.trajects.map(traject => //what ever is your code.... }