<击> 我正在尝试使用React渲染项目列表。关键是项目共享一个共同的状态,可以由每个项目控制。
为简单起见,假设我们有一个字符串数组。我们有一个映射在数组上的List
组件,并生成Item
个组件。每个Item
都有一个按钮,当点击它时,它会改变列表中所有项目的状态(我已经包含了一个代码片段来传达我正在尝试做的事情)。
我将state
存储在List
组件中,并通过Item
将其值传递给每个props
子组件。我遇到的问题是按钮单击(在Item
内)并没有改变UI状态。我认为这个问题与items
在单击按钮时没有改变的事实有关(正确地说是这样),因此React不会重新呈现列表(我本来期望某种UI更新,因为当isEditing
状态发生变化时,传递到Item
的道具List
会发生变化。
如何让React处理这种情况?
注意:单击代码段中的Edit
按钮时似乎存在脚本错误,但是当我在本地运行它时,我没有遇到它。相反,不会抛出任何错误,但UI中的任何内容都不会更新。当我调试它时,我可以看到List
中的状态变化没有传播给它的子节点
击>
鉴于最初的问题不够明确,我在下面重述它。
我想在React
中呈现项目列表。每个项目都应显示一个单词和Edit
按钮。用户应该只能一次编辑一个项目。
Edit
按钮。Edit
时,只有第1项可编辑,Edit
按钮变为Save
按钮。列表中的其他项目不应再显示相应的Edit
按钮。Save
后,将显示该项目的新值。所有编辑按钮(对于其余项目)应该再次可见。在我原来的实现中,我在父组件(List
)中存储了一个 edit 状态,但是这个状态没有正确传播到它的Item
个孩子
注意:我的原始实现缺乏状态管理逻辑,我后来发现它是主要的罪魁祸首(请参阅下面的回复)。它还有一个 bind 错误,如下面@Zhang所述。我将它留在这里供将来参考,虽然它不是一个很好的例子。
这是我最初的实现:
const items = ['foo', 'bar'];
class List extends React.Component {
constructor(props) {
super(props);
this.state = {
isEditing: false
};
}
toggleIsEditing() {
this.setState((prevState) => {
return {
isEditing: !prevState.isEditing
}
});
}
render() {
return (
<ul>
{items.map((val) => (
<Item value={val}
toggleIsEditing={this.toggleIsEditing}
isEditing={this.state.isEditing}/>
))}
</ul>
);
}
}
class Item extends React.Component {
render() {
return (
<li>
<div>
<span>{this.props.value}</span>
{ !this.props.isEditing &&
(<button onClick={this.props.toggleIsEditing}>
Edit
</button>)
}
{ this.props.isEditing &&
(<div>
<span>...Editing</span>
<button onClick={this.props.toggleIsEditing}>
Stop
</button>
</div>)
}
</div>
</li>
);
}
}
ReactDOM.render(<List />, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<body>
<div id="app" />
</body>
答案 0 :(得分:0)
在将toggleIsEditing传递给子组件
时,您没有绑定父作用域
<Item value={val}
toggleIsEditing={this.toggleIsEditing.bind(this)}
isEditing={this.state.isEditing}/>
答案 1 :(得分:0)
当我通过重新思考我的实施来改写我的问题时,我想出了解决方案。我的原始实现有一些问题:
this
类中非生命周期方法中的List
未绑定到类范围(如@ZhangBruce在其答案中所述)。List
中的状态管理逻辑缺乏能够处理用例的其他属性。此外,我认为将state
添加到Item
组件本身对于正确传播更新非常重要。具体来说,添加state.val
是关键(根据我的理解)。可能还有其他方式(可能更简单),在这种情况下我很想知道,但与此同时我的解决方案:
const items = ['foo', 'bar'];
class List extends React.Component {
constructor(props) {
super(props);
this.state = {
editingFieldIndex: -1
};
}
setEdit = (index = -1) => {
this.setState({
editingFieldIndex: index
});
}
render() {
return (
<ul>
{items.map((val, index) => (
<Item val={val}
index={index}
setEdit={this.setEdit}
editingFieldIndex={this.state.editingFieldIndex} />
))}
</ul>
);
}
}
class Item extends React.Component {
constructor(props) {
super(props);
this.state = {
val: props.val
};
}
save = (evt) => {
this.setState({
val: evt.target.value
});
}
render() {
const { index, setEdit, editingFieldIndex } = this.props;
const { val } = this.state;
const shouldShowEditableValue = editingFieldIndex === index;
const shouldShowSaveAction = editingFieldIndex === index;
const shouldHideActions =
editingFieldIndex !== -1 && editingFieldIndex !== index;
const editableValue = (
<input value={val} onChange={(evt) => this.save(evt)}/>
)
const readOnlyValue = (
<span>{val}</span>
)
const editAction = (
<button onClick={() => setEdit(index)}>
Edit
</button>
)
const saveAction = (
<button onClick={() => setEdit()}>
Save
</button>
)
return (
<li>
<div>
{ console.log(`index=${index}`) }
{ console.log(`editingFieldIndex=${editingFieldIndex}`) }
{ console.log(`shouldHideActions=${shouldHideActions}`) }
{
shouldShowEditableValue
? editableValue
: readOnlyValue
}
{
!shouldHideActions
? shouldShowSaveAction
? saveAction
: editAction
: ""
}
</div>
</li>
);
}
}
ReactDOM.render(<List />, document.getElementById('app'));
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<body>
<div id="app" />
</body>
&#13;