Entendiendo React ‘setState’
Los componentes de React pueden, y a menudo tienen, estado. El estado puede ser cualquier cosa, pero piense en cosas como si un usuario ha iniciado sesión o no y muestra el nombre de usuario correcto en función de la cuenta que está activa. O una variedad de publicaciones de blog. O si un modal está abierto o no y qué pestaña dentro de él está activa.
Componentes de React con interfaz de usuario de renderizado de estado basada en ese estado. Cuando el estado de los componentes cambia, también lo hace la interfaz de usuario del componente.
Eso hace que entender cuándo y cómo cambiar el estado de tu componente sea importante. Al final de este tutorial, debe saber cómo funciona setState
y ser capaz de evitar los errores comunes que muchos de nosotros encontramos al aprender React.
El funcionamiento de`setState () ‘
setState()
es la única forma legítima de actualizar el estado después de la configuración inicial del estado. Digamos que tenemos un componente de búsqueda y queremos mostrar el término de búsqueda que envía un usuario.
Aquí está la configuración:
import React, { Component } from 'react'class Search extends Component { constructor(props) { super(props) state = { searchTerm: '' } }}
estamos pasando una cadena vacía como valor y, para actualizar el estado de searchTerm
, tenemos que llamar a setState()
.
setState({ searchTerm: event.target.value })
Aquí, estamos pasando de un objeto a setState()
. El objeto contiene la parte del estado que queremos actualizar que, en este caso, es el valor de searchTerm
. React toma este valor y lo fusiona en el objeto que lo necesita. Es algo así como el componente Search
pregunta qué debe usar para el valor de searchTerm
y setState()
responde con una respuesta.
Esto es básicamente el inicio de un proceso que React llama a la reconciliación. El proceso de reconciliación es la forma en que React actualiza el DOM, al realizar cambios en el componente en función del cambio de estado. Cuando se activa la solicitud a setState()
, React crea un nuevo árbol que contiene los elementos reactivos en el componente (junto con el estado actualizado). Este árbol se usa para averiguar cómo debe cambiar la interfaz de usuario del componente Search
en respuesta al cambio de estado comparándolo con los elementos del árbol anterior. React sabe qué cambios implementar y solo actualizará las partes del DOM cuando sea necesario. Es por eso que React es rápido.
Eso suena como mucho, pero para resumir el flujo de:
- Tenemos un componente de búsqueda que muestra un término de búsqueda
- Ese término de búsqueda está actualmente vacío
- El usuario envía un término de búsqueda
- Ese término es capturado y almacenado por
setState
como un valor - Se lleva a cabo la reconciliación y React nota el cambio en el valor
- React instruye al componente de búsqueda para actualizar el valor y el término de búsqueda se fusiona en
El proceso de reconciliación no cambia necesariamente todo el árbol, excepto en una situación en la que la raíz del árbol se cambia de esta manera:
// old<div> <Search /></div>// new<span> <Search /></span>
Todas las etiquetas <div>
se convierten en etiquetas <span>
y todo el árbol de componentes se actualizará como resultado.
La regla general es nunca mutar el estado directamente. Utilice siempre setState()
para cambiar de estado. Modificar el estado directamente, como el fragmento de código de abajo, no hará que el componente se vuelva a renderizar.
// do not do thisthis.state = { searchTerm: event.target.value}
Pasar una función a`setState () ‘
Para demostrar más esta idea, vamos a crear un contador simple que aumenta y disminuye al hacer clic.
Ver el bolígrafo setState de Kingsley Silas Chijioke (@kinsomicrote) en CodePen.
Registremos el componente y definamos el marcado para la interfaz de usuario:
class App extends React.Component {state = { count: 0 }handleIncrement = () => { this.setState({ count: this.state.count + 1 })}handleDecrement = () => { this.setState({ count: this.state.count - 1 })} render() { return ( <div> <div> {this.state.count} </div> <button onClick={this.handleIncrement}>Increment by 1</button> <button onClick={this.handleDecrement}>Decrement by 1</button> </div> ) }}
En este punto, el contador simplemente incrementa o disminuye el recuento en 1 en cada clic.
Pero, ¿y si quisiéramos aumentar o disminuir en 3 en su lugar? Podríamos intentar llamar a setState()
tres veces en el handleDecrement
y handleIncrement
funciones como esta:
handleIncrement = () => { this.setState({ count: this.state.count + 1 }) this.setState({ count: this.state.count + 1 }) this.setState({ count: this.state.count + 1 })}handleDecrement = () => { this.setState({ count: this.state.count - 1 }) this.setState({ count: this.state.count - 1 }) this.setState({ count: this.state.count - 1 })}
Si está programando en casa, es posible que se sorprenda al descubrir que no funciona.
El fragmento de código anterior es equivalente a:
Object.assign( {}, { count: this.state.count + 1 }, { count: this.state.count + 1 }, { count: this.state.count + 1 },)
Object.assign()
se utiliza para copiar datos de un objeto de origen a un objeto de destino. Si todos los datos que se copian del origen al destino tienen las mismas claves, como en nuestro ejemplo, el último objeto gana. Aquí hay una versión más simple de cómo funciona Object.assign()
;
let count = 3const object = Object.assign({}, {count: count + 1}, {count: count + 2}, {count: count + 3});console.log(object);// output: Object { count: 6 }
Así que en lugar de que la llamada se realice tres veces, solo se realice una vez. Esto se puede arreglar pasando una función a setState()
. Al igual que pasa objetos a setState()
, también puede pasar funciones, y esa es la forma de salir de la situación anterior.
Si editamos la función handleIncrement
para que se vea así:
handleIncrement = () => { this.setState((prevState) => ({ count: prevState.count + 1 })) this.setState((prevState) => ({ count: prevState.count + 1 })) this.setState((prevState) => ({ count: prevState.count + 1 }))}
now ahora podemos incrementar el recuento tres veces con un solo clic.
En este caso, en lugar de fusionar, React pone en cola las llamadas a la función en el orden en que se realizan y actualiza todos los estados en los que se realiza. Esto actualiza el estado de conteo a 3 en lugar de 1.
Acceda al Estado anterior Usando Updater
Al compilar aplicaciones de React, hay ocasiones en las que querrá calcular el estado anterior del componente en función del estado. No siempre puede confiar en this.state
para mantener el estado correcto inmediatamente después de llamar a setState()
, ya que siempre es igual al estado representado en la pantalla.
Volvamos a nuestro ejemplo de contador para ver cómo funciona esto. Digamos que tenemos una función que disminuye nuestra cuenta en 1. Esta función se ve así:
changeCount = () => { this.setState({ count: this.state.count - 1})}
Lo que queremos es la capacidad de disminuir en 3. La función changeCount()
se llama tres veces en una función que maneja el evento de clic, como este.
handleDecrement = () => { this.changeCount() this.changeCount() this.changeCount()}
Cada vez que se hace clic en el botón para disminuir, el recuento disminuirá en 1 en lugar de 3. Esto se debe a que this.state.count
no se actualiza hasta que el componente se ha vuelto a renderizar. La solución es usar un actualizador. Un actualizador le permite acceder al estado actual y usarlo inmediatamente para actualizar otros elementos. Así que la función changeCount()
se verá así.
changeCount = () => { this.setState((prevState) => { return { count: prevState.count - 1} })}
Ahora no estamos dependiendo del resultado de this.state
. Los estados de count
se construyen unos sobre otros, de modo que podemos acceder a el correcto estado que cambia con cada llamada a changeCount()
.
setState()
debe tratarse de forma asincrónica, en otras palabras, no siempre espere que el estado haya cambiado después de llamar a setState()
.
Terminar
Cuando se trabaja con setState()
, estas son las cosas principales que debe saber:
- La actualización a un estado de componente debe hacerse utilizando
setState()
- Puede pasar un objeto o una función a
setState()
- Pase una función cuando pueda actualizar el estado varias veces
- No dependa de esto.estado inmediatamente después de llamar a
setState()
y hacer uso de la función de actualización en su lugar.