setState no está actualizando el estado correctamente

Lo siento, realmente echo de menos algo con la transmisión de state dentro de los props de los subcomponentes en React.

He implementado una versión de una lista de tareas con 3 componentes.

Hay un componente Form y un componente ListTodo . El estado se almacena sólo en el componente de la 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 ( 
); } } 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 ( ) } } class ListTodo extends Component { render() { const tasks = this.props.tasks.map((t, i) => (
  • {t}
  • )) return (
      {tasks}
    ) } } export default App;

    La pantalla es buena al inicio, por lo que ListTodo logra ver las tasks prop. Pero después de enviar un formulario, recibo un error en ListTodo.render :

    TypeError: this.props.tasks.map no es una función

    Cuando this.props.tasks the this.props.tasks , no obtengo mi matriz, sino la longitud de la matriz.

    ¿Sabes por qué?

    Edit : Gracias por las respuestas chicos, tienes razón. Me perdí el comportamiento de Array.push.

    Pero React parece aún extraño. Si dejo el código erróneo this.setState({ tasks: this.state.tasks.push(task) })

    luego, una console.log(JSON.stringify(this.state)) muestra:

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

    Muy molesto para no poder confiar en una console.log …

    Según MDN DOC :

    El método push () agrega uno o más elementos al final de una matriz y devuelve la nueva longitud de la matriz .

    Array.push nunca devuelve la matriz de resultados, devuelve el número, por lo que, después de agregar la primera tarea, this.state.tasks convierte en un número y está this.state.tasks el error cuando intenta ejecutar el mapa en el número.

    Usted cometió el error aquí:

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

    Escríbelo así:

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

    Otra cosa importante aquí es que el nuevo estado dependerá del valor del estado anterior, por lo que en lugar de usar this.state dentro de setState, use la función de actualización.

    Explicación sobre la parte de edición:

    Dos cosas importantes están sucediendo allí:

    1- setState es asíncrono, de modo que justo después de setState podemos esperar el valor de estado actualizado.

    Compruebe esto para obtener más detalles sobre el comportamiento asíncrono de setState .

    2- Array.push siempre muta la matriz original empujando el elemento hacia eso, por lo que está mutando directamente el valor del estado por this.state.tasks.push() .


    Compruebe el DOC para más detalles sobre setState.

    Compruebe el MDN Doc para el spread operator (...) .

    Compruebe este fragmento de código:

     let a = [1,2,3,4]; let b = a.push(5); //it will be a number console.log('a = ', a); console.log('b = ', b); 

    El problema está en cómo agregar una tarea en el estado de la aplicación.

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

    Ver, Array.prototype.push devuelve la longitud de la matriz después de agregar un elemento. Lo que realmente quieres es probablemente Array.prototype.concat .

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

    Además, gracias a los punteros @ tj-crowder y como también lo informa @ mayank-shukla, debe utilizar un enfoque diferente para mutar su estado:

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

    O usando ES2015 flechas, desestructuración de objetos y dispersión de matriz:

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

    Dado que Component.prototype.setState puede ser asíncrono, pasarle una función garantizará que los nuevos valores de estado dependan de los valores correctos, actuales y anteriores .

    Esto significa que si dos o más llamadas a setState se suceden una tras otra, está seguro de que el resultado de la primera se mantendrá aplicando la segunda.

    Como indicaron las otras respuestas, el método de push matriz no devuelve la matriz. Solo para complementar las respuestas anteriores, si está utilizando ES6, una forma agradable y elegante de hacerlo es mediante el operador de propagación (puede leer más sobre esto aquí )

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

    Es esencialmente lo mismo que usar el método concat , pero creo que esto tiene una mejor legibilidad.

    El problema es que Array.push devuelve el número de elementos en la matriz y no la matriz actualizada

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

    Para solucionarlo, puede enviar a state.tasks y luego establecerState con él más adelante:

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

    De esta manera usted establece state.task a la matriz actualizada.