setState is not updating state properly

Viewed 1584

Sorry, I really miss something with the transmission of state within props of sub components in React.

I have implemented a version of a todo list with 3 components.

There is a Form component and a ListTodo component. The state is stored only in the App component.

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;

The display is good at start so the ListTodo achieves to see the prop tasks. But after a form submission, I get an error on ListTodo.render :

TypeError: this.props.tasks.map is not a function

When I console.log the this.props.tasks, I don't get my array but the length of the array.

Do you know why?

Edit : Thanks for answers guys, you're right. I missed the behavior of Array.push.

But React seems still odd. If I let the mistaken code this.setState({ tasks: this.state.tasks.push(task) })

then a console.log(JSON.stringify(this.state)) displays :

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

Very disturbing to not be able to trust a console.log...

4 Answers

The problem is from Array.push return the number of elements in the array and not the updated array

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

To fix this you can push to state.tasks then setState with it later on:

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

This way you set state.task to the updated array.

Related