¿Cuál es el uso de la sintaxis funcional de setState en reaccionar componentes funcionales? [duplicar]
estamos hablando de componentes funcionales que tienen useState
digamos
const [age, setAge] = useState(0)
ahora digamos que mientras actualizo age
tengo que usar el anteriorage
Los documentos de React mencionan algo llamado ACTUALIZACIONES FUNCIONALES donde puede pasar una función y el argumento será el valor anterior del estado, por ejemplo.
setState((previousAge) => previousAge + 1)
¿Por qué necesito hacer esto cuando puedo hacerlo?
setState(previousAge + 1)
¿Cuáles son los beneficios de usar funcional setState
,
Sé que en los componentes basados en clases había algo llamado agrupamiento de actualizaciones de estado en la forma funcional, pero no puedo encontrar nada de eso en la documentación de componentes funcionales.
Respuestas
No son lo mismo, si su actualización depende de un valor anterior encontrado en el estado, entonces debe usar el formulario funcional. Si no usa el formulario funcional en este caso, su código se romperá en algún momento.
¿Por qué se rompe y cuándo?
Los componentes funcionales de React son solo cierres, el valor de estado que tiene en el cierre puede estar desactualizado; lo que significa esto es que el valor dentro del cierre no coincide con el valor que está en el estado de React para ese componente, esto podría suceder en el siguientes casos:
1- operaciones asíncronas ( en este ejemplo, haga clic en Agregar lento y luego haga clic varias veces en el botón Agregar, luego verá que el estado se restableció a lo que estaba dentro del cierre cuando se hizo clic en el botón Agregar lento)
const App = () => {
const [counter, setCounter] = useState(0);
return (
<>
<p>counter {counter} </p>
<button
onClick={() => {
setCounter(counter + 1);
}}
>
immediately add
</button>
<button
onClick={() => {
setTimeout(() => setCounter(counter + 1), 1000);
}}
>
Add
</button>
</>
);
};
2- Cuando llamas a la función de actualización varias veces en el mismo cierre
const App = () => {
const [counter, setCounter] = useState(0);
return (
<>
<p>counter {counter} </p>
<button
onClick={() => {
setCounter(counter + 1);
setCounter(counter + 1);
}}
>
Add twice
</button>
</>
);
}
Pueden surgir problemas dependiendo de la rapidez / frecuencia con la que se llame a su colocador.
Si está utilizando la forma simple obteniendo el valor del cierre, es posible que las llamadas posteriores entre dos representaciones no tengan el efecto deseado.
Un simple ejemplo:
function App() {
const [counter, setCounter] = useState(0);
const incWithClosure = () => {
setCounter(counter + 1);
};
const incWithUpdate = () => {
setCounter(oldCounter => oldCounter + 1);
};
return (<>
<button onClick={_ => { incWithClosure(); incWithClosure(); }}>
Increment twice using incWithClosure
</button>
<button onClick={_ => { incWithUpdate(); incWithUpdate(); }}>
Increment twice using incWithUpdate
</button>
<p>{counter}</p>
</>);
}
Ambos botones llaman a uno de los métodos de incremento dos veces. Pero observamos:
- El primer botón incrementará el contador solo en 1
- El segundo botón incrementará el contador en 2, que probablemente sea el resultado deseado.
¿Cuándo puede pasar esto?
- Obviamente, si
incWithClosure
se llama varias veces inmediatamente después de la otra - Si hay tareas asincrónicas involucradas, esto puede suceder fácilmente (ver más abajo)
- Quizás, si React tiene mucho trabajo por hacer, sus algoritmos de programación pueden decidir manejar múltiples clics muy rápidos usando el mismo controlador de eventos.
Ejemplo con trabajo asincrónico (simulando cargar un recurso):
function App() {
const [counter, setCounter] = useState(0);
const incWithClosureDelayed = () => {
setTimeout(() => {
setCounter(counter + 1);
}, 1000);
};
const incWithUpdateDelayed = () => {
setTimeout(() => {
setCounter((oldCounter) => oldCounter + 1);
}, 1000);
};
return (
<>
<button onClick={(_) => incWithClosureDelayed()}>
Increment slowly using incWithClosure
</button>
<button onClick={(_) => incWithUpdateDelayed()}>
Increment slowly using incWithUpdate
</button>
<p>{counter}</p>
</>
);
}
Haga clic en el primer botón dos veces (en un segundo) y observe que el contador se incrementa solo en 1. El segundo botón tiene el comportamiento correcto.
Porque si no lo hace , en algún momento encontrará que obtiene un valor anterior age
. El problema es que, a veces, lo que sugieres funcionará. Pero a veces no será así. Es posible que no se rompa en su código actual hoy, pero puede que se rompa en un código diferente que escribió hace unas semanas o en su código actual dentro de unos meses.
El síntoma es realmente extraño. Puede imprimir el valor de la variable dentro de un componente jsx usando la {x}
sintaxis y luego imprimir la misma variable usando console.log
después de renderizar el componente jsx (no antes) y encontrar que el console.log
valor está obsoleto; lo console.log
que sucede después de que el renderizado puede de alguna manera tener un valor más antiguo que el render.
Por lo tanto, es posible que el valor real de las variables de estado no siempre funcione correctamente en el código normal; solo están diseñadas para devolver el último valor en un render. Por esta razón, el mecanismo de devolución de llamada en un establecedor de estado se implementó para permitirle obtener el último valor de una variable de estado en código regular fuera de un render.