setState没有正确更新状态

时间:2017-09-12 14:56:38

标签: javascript reactjs

抱歉,我真的很想念React中state个子组件中props的传输。

我已经实现了一个包含3个组件的待办事项列表版本。

有一个Form组件和一个ListTodo组件。状态仅存储在App组件中。

import React, {Component} from 'react';
import './App.css';

class App extends Component {

    constructor(props) {
        super(props);
        this.state = {
            tasks: ["un truc", "autre truc"]
        };
        this.addTask = this.addTask.bind(this);
    }

    addTask(task) {
        this.setState({
            tasks: this.state.tasks.push(task)
        })
    }

    render() {
        return (
            <div className="App">
                <Form onTaskAdded={ this.addTask }></Form>
                <ListTodo tasks={ this.state.tasks }></ListTodo>
            </div>
        );
    }
}

class Form extends Component {

    constructor(props) {
        super(props);
        this.state = {
            task: ""
        }

        this.handleChange = this.handleChange.bind(this);
        this.addTask = this.addTask.bind(this);
    }

    handleChange(event) {
        this.setState({
            task: event.target.value
        });
    }

    addTask(event) {
        this.props.onTaskAdded(this.state.task);
        event.preventDefault();
    }

    render() {
        return (
            <form onSubmit={ this.addTask }>
                <input placeholder="À faire" onChange={ this.handleChange }></input>
                <input type="submit"></input>
            </form>
        )
    }
}

class ListTodo extends Component {

    render() {
        const tasks = this.props.tasks.map((t, i) => (
                <li key={i}>{t}</li>
            ))

        return (
            <ul>{tasks}</ul>
        )
    }
}

export default App;

显示效果很好,ListTodo可以看到道具tasks。但在提交表单后,我在ListTodo.render上收到错误:

  

TypeError:this.props.tasks.map不是函数

当我在console.log this.props.tasks时,我没有得到我的数组,而是数组的长度。

你知道为什么吗?

修改: 谢谢你的回答,你说得对。我错过了Array.push的行为。

但React似乎仍然很奇怪。如果我让错误的代码 this.setState({ tasks: this.state.tasks.push(task) })

然后显示console.log(JSON.stringify(this.state))

{"tasks":["un truc","autre truc","aze"]}

非常不安,无法信任console.log ......

4 个答案:

答案 0 :(得分:6)

根据MDN DOC

  

push()方法将一个或多个元素添加到数组的末尾   返回数组的新长度

Array.push永远不会返回结果数组,它返回数字,因此在添加第一个任务后,this.state.tasks变为一个数字,并且当您尝试在数字上运行地图时它会抛出错误。

你在这里犯了错误:

addTask(task) {
    this.setState({
        tasks: this.state.tasks.push(task)
    })
}

像这样写:

addTask(task) {
    this.setState( prevState => ({
        tasks: [...prevState.tasks, task]
    }))
}

这里的另一个导入是,新状态将取决于先前的状态值,因此不使用setState内的this.state,而是使用updater函数。

关于编辑部分的说明:

那里发生了两件重要的事情:

1 - setState是异步的,所以在setState之后我们可以期待更新的状态值。

查看此内容,详细了解 async behaviour of setState

2 - Array.push总是通过将项目推入该数组来改变原始数组,因此您可以通过this.state.tasks.push()直接改变状态值。

检查 DOC ,了解有关setState的详细信息。

检查 MDN Doc 以查找spread operator (...)

检查此代码段:

let a = [1,2,3,4];

let b = a.push(5);  //it will be a number

console.log('a = ', a);

console.log('b = ', b);

答案 1 :(得分:2)

问题出在你如何在应用程序状态中添加任务

addTask(task) {
    this.setState({
        tasks: this.state.tasks.push(task)
    })
}

添加元素后,请参阅数组的Array.prototype.push returns the length。你真正想要的是probably Array.prototype.concat

addTask(task) {
    this.setState({
        tasks: this.state.tasks.concat([ task ])
    })
}

另外,感谢@ t-j-crowder指针以及@ mayank-shukla报道,你应该使用不同的方法来改变你的状态:

addTask(task) {
    this.setState(function (state) {
        return {
            tasks: state.tasks.concat([ task ])
        }
    });
}

或使用ES2015箭头,对象解构和数组传播:

addTask(task) {
    this.setState(({ tasks }) => ({
        tasks: [ ...tasks, task ]
    }));
}

由于Component.prototype.setState 可以异步,向其传递函数将保证新状态值取决于右,当前先前值< /强>

这意味着,如果两个或多个setState次呼叫一个接一个地发生,您可以确保通过应用第二个来保留第一个呼叫的结果。

答案 2 :(得分:1)

正如其他答案所述,Array push方法不返回数组。只是为了补充上面的答案,如果您使用的是ES6,那么使用扩展运算符(您可以阅读更多关于它的here)这是一种优雅而优雅的方式。

this.setState({
    tasks: [...this.state.tasks, task]
})

它与使用concat方法基本相同,但我认为它具有更好的可读性。

答案 3 :(得分:0)

问题出在Array.push中,返回数组中元素的数量,而不是更新后的数组

addTask(task) {
  this.setState({
    tasks: this.state.tasks.push(task)
  })
}

要解决此问题,您可以推送至state.tasks,然后在其后设置setState:

addTask(task) {
  this.state.tasks.push(task);
  this.setState({
    tasks: this.state.tasks
  })
}

通过这种方式将state.task设置为更新后的数组。