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.