我正在建立反应中的todo应用程序,我在同时更新子项和父组件的状态(通过由prop传入的方法)时遇到问题。当我单击single-task.js中的编辑按钮时,我想触发setState({isEditing:true})和this.props.globalEditingToggle(); (由app.js通过list.js传递)。它一直工作得很好,直到我意识到我在list.js中设置键值时犯了一个错误。我曾经这样设置:key = {i}。然后我使用shortid将其切换为唯一ID。并且只有this.props.globalEditingToggle();触发。为什么会这样,什么可以解决?
这是一段代码:
app.js:
import {List} from './list';
export class App extends React.Component{
constructor(props){
super(props);
this.state={
tasks: [['Task no.1',false],['Task no.2',true],['Task no.3',false]],
isGlobalEditing: false,
}
this.globalEditingToggle=this.globalEditingToggle.bind(this);
this.updateTasks=this.updateTasks.bind(this);
this.delete=this.delete.bind(this);
}
globalEditingToggle(){
let newState=this.state.isGlobalEditing===false?true:false;
this.setState({isGlobalEditing: newState});
}
updateTasks(input,item){
if(this.state.isAdding){
this.addingToggle();
const updatedTasks=this.state.tasks;
updatedTasks.push([input, false]);
this.setState({tasks: updatedTasks});
}else{
const updatedTasks=this.state.tasks;
updatedTasks.splice(item,1,input);
this.setState({tasks: updatedTasks});
}
}
delete(e){
const item=e.target.parentNode.parentNode.id;
const updatedTasks=this.state.tasks;
updatedTasks.splice(item,1);
this.setState({tasks: updatedTasks});
}
render(){
return (
<div className='gridContainer'>
<div className='gridHeader'>
<h1 className='title'>Tasks to do</h1>
</div>
<List
tasks={this.state.tasks}
isGlobalEditing={this.state.isGlobalEditing}
globalEditingToggle={this.globalEditingToggle}
delete={this.delete}
updateTasks={this.updateTasks}/>
</div>
);
}
}
list.js:
import {SingleTask} from './single-task';
import shortid from 'shortid';
export class List extends React.Component{
render(){
let list=[];
for(let i in this.props.tasks){
list.push(
<SingleTask
key={shortid.generate()}
tasks={this.props.tasks}
i={i}
isGlobalEditing={this.props.isGlobalEditing}
globalEditingToggle={this.props.globalEditingToggle}
updateTasks={this.props.updateTasks}
delete={this.props.delete}
/>
);
}
return(
<div className='gridMain'>
{list}
</div>
);
}
}
单task.js:
export class SingleTask extends React.Component{
constructor(props){
super(props);
this.state={
isEditing: false
}
this.editOn=this.editOn.bind(this);
this.editOff=this.editOff.bind(this);
this.save=this.save.bind(this);
}
editOn(){
this.setState({isEditing: true});
this.props.globalEditingToggle();
}
editOff(){
this.setState({isEditing: false});
this.props.globalEditingToggle();
}
save(e){
const newText=e.target.parentNode.parentNode.querySelector('.editField').value;
const status=this.props.tasks[e.target.parentNode.parentNode.id][1];
const item=e.target.parentNode.parentNode.id;
this.props.updateTasks([newText, status],item);
this.editOff();
}
render(){
if(this.state.isEditing){
return (
<div id={this.props.i} className='taskContainer'>
<label className='check'>
<input type='checkbox'/>
</label>
<input type='text' defaultValue={this.props.tasks[this.props.i][0]} className='editField'/>
<div className='menu'>
<button onClick={this.save}>Save</button>
<button onClick={this.editOff}>Cancel</button>
</div>
</div>
);
}else{
return (
<div id={this.props.i} className='taskContainer'>
<label className='check'>
<input type='checkbox'/>
</label>
<span className='main'>{this.props.tasks[this.props.i]}</span>
<div className='menu'>
<button onClick={this.editOn}>Edit</button>
<button onClick={this.props.delete}>Delete</button>
</div>
</div>
);
}
}
}
答案 0 :(得分:0)
我认为发生的事情是你在渲染时调用shortid
,而不是在创建任务时使用它。
对于<App />
组件中的每个更改,您的整个列表将重新呈现,因为每次重新渲染都会为<SingleTask />
组件生成唯一的ID,您将卸载并安装全新的<SingleTask />
元素,因此在其中任何一个元素中,您都将获得初始状态:{ isEditing: false }
。
答案 1 :(得分:0)
key
将DOM元素映射到其虚拟DOM元素。当您<List>
呈现时,您尝试设置的密钥为key={shortid.generate()}
。因此,每次设置新密钥并删除旧元素并装入新元素时。
由于再次安装了新元素,因此您的状态将设置为其初始状态。要进一步确认组件是否已卸载并再次装入,您可以在console.log
下的相应功能中尝试SingleTask
。
解决方案是,您必须使用shortid
仅为id
生成task
一次,最好是在创建任务时(以及在初始化任务时)最初的状态)。