setState由在props中传递的方法中断,该方法设置父组件的状态

时间:2018-02-23 18:00:48

标签: javascript reactjs

我正在建立反应中的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>
        );
    }
}
}

2 个答案:

答案 0 :(得分:0)

我认为发生的事情是你在渲染时调用shortid,而不是在创建任务时使用它。

对于<App />组件中的每个更改,您的整个列表将重新呈现,因为每次重新渲染都会为<SingleTask />组件生成唯一的ID,您将卸载并安装全新的<SingleTask />元素,因此在其中任何一个元素中,您都将获得初始状态:{ isEditing: false }

答案 1 :(得分:0)

React使用

key将DOM元素映射到其虚拟DOM元素。当您<List>呈现时,您尝试设置的密钥为key={shortid.generate()}。因此,每次设置新密钥并删除旧元素并装入新元素时。

由于再次安装了新元素,因此您的状态将设置为其初始状态。要进一步确认组件是否已卸载并再次装入,您可以在console.log下的相应功能中尝试SingleTask

解决方案是,您必须使用shortid仅为id生成task一次,最好是在创建任务时(以及在初始化任务时)最初的状态)。