Tengo el siguiente código :-
const [dice, setDice] = React.useState(allNewDice()) // Array of objects with the following properties: `value` (random digit between 1 and 6), `id` (unique id), `isHeld` (boolean) function holdDice(id) { const heldDie = dice.findIndex(die => die.id === id) setDice(prevDice => { prevDice[heldDie].isHeld = !prevDice[heldDie].isHeld return prevDice }) } La función holdDice está funcionando como se esperaba. Actualiza la propiedad prevDice del primer elemento de isHeld al contrario de lo que era anteriormente, como es evidente al iniciar sesión en la consola. Pero el único problema es que el estado no se actualiza.
Así que intenté hacer una copia de prevDice y luego devolver la copia (usando el operador de propagación ( ... )): -
const [dice, setDice] = React.useState(allNewDice()) // Array of objects with the following properties: `value` (random digit between 1 and 6), `id` (unique id), `isHeld` (boolean) function holdDice(id) { const heldDie = dice.findIndex(die => die.id === id) setDice(prevDice => { prevDice[heldDie].isHeld = !prevDice[heldDie].isHeld return [...prevDice] }) }Y esto funciona perfectamente.
¿Alguien puede explicar por qué este comportamiento? ¿Por qué no puedo devolver el estado anterior para que se establezca como el nuevo estado, incluso cuando no estoy devolviendo el estado anterior, exactamente como estaba?
Entiendo que es costoso volver a renderizar el componente y que tal vez React no actualice el estado cuando el estado anterior y el nuevo son iguales. Pero aquí, el estado anterior y el nuevo no son iguales.
Lo primero que supuse fue que podría haber estado relacionado con React, sin verificar en profundidad si los objetos de la matriz prevDice se han cambiado o no, o algo así. Así que creé una matriz de Number e intenté cambiar su primer elemento a algún otro número, pero a menos que devolviera una copia de la matriz, el estado aún no cambió.
Cualquier ayuda sería muy apreciada. ¡Gracias!
La primera vez que llama a setDice() después del montaje inicial de su componente o renderizado, prevDice se refiere a la misma matriz en la memoria a la que se refiere su estado de dice (entonces prevDice === dice ). No es una copia única de la matriz que puede modificar. No puede/no debe modificarlo porque el objeto al que se refiere prevDice es el mismo objeto al que se refiere el dice , por lo que en realidad está modificando el estado del dice directamente cuando cambia prevState . Debido a esto, cuando se usa prevDice modificado como valor para setDice() , React verifica si el nuevo estado (que es prevState ) es diferente del estado actual ( dice ) para ver si necesita volver a renderizarse. React hace esta verificación de igualdad usando === (o más específicamente Object.is() ) para verificar si las dos matrices/objetos son iguales y, si lo son, no se vuelve a representar. Cuando React usa === entre prevState y dice , JS verifica si ambas variables se refieren a las mismas matrices/objetos. En nuestro caso lo son, por lo que React no se vuelve a renderizar. Es por eso que es muy importante tratar su estado como inmutable , lo que puede pensar que significa que debe tratar su estado como de solo lectura y, en cambio, si desea cambiarlo, puede hacer una "copia" de él y cambiar el Copiar. Si lo hace, le indicará correctamente a React que use esa nueva copia modificada como el nuevo estado cuando se pase a setDice() .
Tenga en cuenta que si bien su segundo ejemplo funciona, todavía está modificando su estado directamente, y esto aún puede causar problemas . Su segundo ejemplo funciona porque la matriz que está devolviendo es una nueva matriz [] , pero el contenido de esa matriz (es decir, las referencias del objeto) todavía se refiere a los mismos objetos en la memoria a los que se refería su matriz de estado de dice original. Para realizar correctamente su actualización de forma inmutable, puede usar algo como .map() , que por naturaleza devuelve una nueva matriz (por lo que volveremos a renderizar). Cuando mapea, puede devolver un nuevo objeto interno cuando encuentre el que desea actualizar (en lugar de actualizarlo directamente). De esta manera, su matriz es una matriz nueva y cualquier objeto actualizado también es nuevo:
function holdDice(id) { setDice(prevDice => prevDice.map( // `.map()` returns a new array, so different to the `dice`/`prevDice` state die => die.id === id ? {...die, isHeld: !die.isHeld} // new object, with overwritten property `isHeld` : die )); }El estado no se actualiza porque lo actualiza usted mismo en la función setDice. dice y prevDice se refieren al mismo objeto, por lo que no activará una nueva representación.
const [dice, setDice] = React.useState(allNewDice()) function holdDice(id) { const heldDie = dice.findIndex(die => die.id === id) setDice(prevDice => { // Create a copy of the dice // Use whatever method you wish.Spread. JSON parse and stringify. const currentDice = JSON.parse(JSON.stringify(prevDice)) currentDice[heldDie].isHeld = !prevDice[heldDie].isHeld return currentDice }) }