Cómo acceder a propiedades o estados anteriores con React Hooks

Nota del editor: esta guía fue actualizada por última vez por Marie Starck el 1 de mayo de 2024 para proporcionar una comparación entre propiedades y estado en React, así como para comparar el uso useRefy useStateel seguimiento de valores anteriores, y para responder preguntas frecuentes sobre cómo trabajar con el estado en React, como por ejemplo cómo los Hooks se diferencian de los componentes de clase al manejar el estado.

Acceder a las propiedades o al estado anterior desde un componente funcional es uno de esos problemas aparentemente sencillos que puedes encontrar al trabajar con React Hooks. Aunque actualmente ningún React Hook lo hace de forma predeterminada, puedes recuperar manualmente el estado o las propiedades anteriores desde un componente funcional usando los Hooks useRef, useStatey useEffect. En este artículo, aprenderás cómo.

Antes de comenzar, repasa tus conocimientos sobre React Hooks con nuestra hoja de trucos sobre React Hooks :

Table
  1. ¿Qué es el estado anterior?
    1. ¿Por qué acceder al estado anterior?
  2. Obtener los accesorios o el estado anterior a través deuseRef
  3. Ganchos personalizados con el usePreviousgancho
  4. useRefEn profundidad: Un gancho de React pasado por alto
  5. Recuperando propiedades y estados anteriores conuseRef
  6. Uso de useStatevs. useRefpara rastrear valores anteriores
  7. Propiedades vs. estado en React
  8. ¿El estado y los accesorios se representan al mismo tiempo?
  9. ¿Puedes conseguir un accesorio anterior con un useEffectgancho?
  10. React Hooks vs. componentes de clase para la gestión de estados
  11. Uso de Zustand para la gestión del estado
  12. Conclusión

¿Qué es el estado anterior?

React ofrece un enfoque declarativo para la manipulación de la interfaz de usuario. En lugar de modificar directamente elementos específicos de la interfaz, definimos los distintos estados que puede adoptar nuestro componente mediante variables conocidas como estado.

Los mejores creadores de sitios web sin código para diseñadores

De forma similar, el estado anterior se refiere al valor de una variable de estado antes de su actualización. Cuando se producen varias actualizaciones, el estado anterior se refiere al valor del estado anterior al actual. A continuación, se muestra un ejemplo visual para desglosarlo:

¿Por qué acceder al estado anterior?

Es importante acceder al estado anterior solo por las razones correctas. Por ejemplo, si solo necesitas el estado anterior para limpiar un efecto, deberías confiar en la compatibilidad nativa de React. Considera el siguiente ejemplo:

useEffect(()) = { UserService.subscribe(props.userId); return() = UserService.unsubscribe(props.userId); }, [props.userId]);          

En este ejemplo, userIdno es necesario obtener la propiedad anterior, ya que la función de limpieza la capturará en un cierre. Si userIdcambia de 3a 4, se ejecutará primero antes de .UserService.unsubscribe(3)UserService.subscribe(4)

Por otro lado, si actualiza algún valor de estado en respuesta a cambios en otro estado o propiedad, esto podría indicar redundancia. Considere refactorizar o simplificar la representación de su estado. Esto mantendrá las actualizaciones de sus componentes predecibles y fáciles de entender.

Una descripción general de las líneas base del proyecto

Si su caso de uso no se encuentra entre los mencionados anteriormente, quizás necesite el estado anterior por las razones correctas. Esto suele ser poco común, por ejemplo, al crear un Transitioncomponente. Ahora, consideremos nuestra solución usando useRef.

Obtener los accesorios o el estado anterior a través deuseRef

Considere el siguiente ejemplo anotado con un Countercomponente:

importar { useEffect , useState , useRef } de "react" ; const INITIAL_COUNT = 0 ;    función Counter () { const [ count , setCount ] = useState ( INITIAL_COUNT ); // El gancho useRef le permite conservar datos entre renderizaciones const prevCountRef = useRef  number ( INITIAL_COUNT );   useEffect (() = { /**                     * asignar el último valor de renderizado de count a la referencia     * Sin embargo, asignar un valor a ref no vuelve a renderizar la aplicación.     * Entonces, prevCountRef.current en la declaración de retorno muestra el     * último valor en la referencia en el momento de la representación, es decir, el valor del estado anterior.     */     prevCountRef . current = count ; }, [ count ]); //ejecuta este código cuando cambia el valor de count return (  h1  Now : { count }, before : { prevCountRef . current } { /* Aumentar el conteo al hacer clic */ }  br /  button onClick ={() = setCount (( count ) = count + 1 )} Aumentar el conteo       / button  / h1  ); } export default Counter ;                                                        

El código anterior genera la siguiente salida:

En el bloque de código anotado anterior, actualizamos el countestado como de costumbre mediante la función de actualización de estado. En cuanto se actualiza el estado, también actualizamos el refvalor actual al nuevo valor del estado.

Celebrar una reunión de lanzamiento de diseño que marque el tono

Sin embargo, debido a que reflas asignaciones no vuelven a activar las representaciones de los componentes, el valor en el método de representación (o declaración de retorno) siempre apuntará al valor del estado anterior (el valor de la referencia antes de que hiciéramos la actualización en ).prevCountRef.currentuseEffect

Ganchos personalizados con el usePreviousgancho

Basándonos en la solución anterior, podemos crear una abstracción a través de un usePreviousHook:

función usePrevious ( valor ) { const ref = useRef ();   useEffect (() = {     ref . current = value ; //asigna el valor de ref al argumento },[ value ]); //este código se ejecutará cuando cambie el valor de 'value' return ref . current ; //al final, devuelve el valor de ref actual. } export default usePrevious ;             

Para utilizar el Hook personalizado dentro de su aplicación, escriba el siguiente código:

función Contador () { const [ count , setCount ] = useState ( 0 ); //   mira aquí const prevCount = usePrevious ( count )            devolver  h1  Ahora : { count }, antes : { prevCount } / h1 ; }     

Aunque este ejemplo parece sencillo, ¿puedes explicar cómo usePreviousfunciona el Hook? Analicemos con claridad qué sucede dentro del Hook y cómo se conserva el valor anterior.

Los 5 rasgos de una innovación

useRefEn profundidad: Un gancho de React pasado por alto

Trabajo con Hooks a diario, tanto en proyectos personales como profesionales. En mi experiencia, he descubierto que muy poca gente entiende y aprovecha el useRefHook . Además de ser excelente para gestionar referencias DOM, useRefes un sustituto perfecto para implementar variables de instancia dentro de componentes funcionales.

Además, recuerda datos entre renderizaciones de la interfaz de usuario, lo que permite a los desarrolladores acceder a valores anteriores sobrescritos. Considere el siguiente ejemplo con componentes de clase:

// componente de clase clase Count extiende React . Component {     constructor () { this.specialVariable = " VARIABLE_ESPECIAL " }            render () { devolver nulo } }        

Cada copia instanciada de la Countclase tendrá su propia specialVariablevariable de instancia. El useRefHook puede simular este comportamiento con una característica aún más interesante:

// componente funcionalfunción Count () { const specialVariable = useRef ( "SPECIAL_VARAIBLE" ); //almacena el valor inicial en el gancho specialVariable. return null }        

El useRefgancho recibe un valor inicial para almacenar, es decir , y devuelve un objeto con una propiedad actual . El valor inicial pasado a se guarda en la propiedad del objeto:useRef("INITIAL_VALUE"){current: "INITIAL_VALUE"}useRefcurrentref

función Count () { const specialVariable = useRef ( "SPECIAL_VARAIBLE" ); // specialVariable se resuelve en {current: "SPECIAL_VARIABLE"}   console.log ( specialVariable ); //para fines de depuración, registra su valor return null ; }          

A diferencia de una variable normal, el specialVariableobjeto de referencia no se recalcula al Countvolver a renderizar el componente. Con useRef, el valor guardado en el refobjeto se mantiene igual entre rerenderizaciones. El valor no se pierde ni se recalcula; permanece igual.

Recuperando propiedades y estados anteriores conuseRef

Cabe mencionar que la única forma de actualizar el refobjeto es establecer directamente el valor de la propiedad actual, es decir, . ¿Por qué es importante esta explicación?specialVariable.current = "NEW_SPECIAL_VARIABLE

Bueno, para entender qué está pasando, veamos paso a paso la ejecución de la solución antes mencionada para recuperar propiedades y estados anteriores:

// gancho personalizado para obtener el valor anterior función usePrevious ( valor ) { const ref = useRef ();   useEffect (() = {     ref . current = valor ; },[ valor ]); return ref . current ; }         // la aplicación donde se usa el gancho function Counter () { const [ count , setCount ] = useState ( 0 ); //   mira aquí const prevCount = usePrevious ( count )            devolver  h1  Ahora : { count }, antes : { prevCount }/ h1 ; }   // Cómo se renderiza la aplicación  Counter / 

Al iniciar el proceso de renderizado de la Counteraplicación, primero se invoca el useStateHook y se configuran las variables county . Tenga en cuenta que ahora es :setCountcount0

A continuación, se ejecutará la siguiente línea. El usePreviousgancho se invoca con el valor actual de la variable de estado de conteo 0:

Al invocar el usePreviousHook, React crea una nueva useRefinstancia:

El valor inicial de este Hook es undefined. La consola retorna porque los datos actuales no existen. En este siguiente paso es donde la mayoría de la gente comete un error. React no ejecuta la llamada. En su lugar, se devuelve el valor actual del Hook personalizado, como se muestra a continuación:{current: undefined}useEffect

El useEffectHook se invoca solo después de que se renderice el componente desde el que se llama. En esencia, el valor de retorno del componente debe ejecutarse primero. A continuación, se reanuda la ejecución dentro del componente. Esta vez, la prevCountvariable contiene el valor undefined:

El returnvalor del componente se evalúa como se muestra a continuación:

Este proceso regresa a la pantalla, donde y son y , respectivamente. Para evitar que el navegador impida que se visualicen los cambios del DOM, la llamada dentro del Hook ahora se invoca de forma asíncrona. se invoca después de que se renderice el componente funcional:h1Now: {count}, before: {prevCount}/h1countprevCount0undefineduseEffectusePrevioususeEffect

Dentro de la effectfunción tenemos lo siguiente:

useEffect(() = { ref . current = valor ; },[ valor ]);  

La línea dentro de la useEffectfunción actualiza la propiedad actual del refobjeto a valor. valueahora representa el valor inicial del Hook personalizado. En este caso, el valor es 0. En este flujo actual, recall usePrevioussolo se ha llamado una vez con el valor inicial de 0:

Ahora, la referencia contiene el valor 0. Cuando la count statevariable dentro de la aplicación se actualiza de 0a 1o a un nuevo recuento, la aplicación ejecutará este fragmento de código de nuevo. El usePreviousgancho se invoca con el nuevo valor de estado 1.



Luego, se evalúa la instrucción de retorno , que devuelve [ sub ( …ref.current01ref.currentuseEffect0return

Solo después del renderizado, la useEffectllamada dentro del usePreviousHook se actualiza con el nuevo valor. 1Este ciclo continúa y, de esta forma, siempre se recibirá el valor anterior pasado al Hook personalizado usePrevious.

Para entender por qué podemos acceder a propiedades o estados anteriores de esta manera, debemos recordar lo siguiente:

  • El refobjeto siempre devolverá el mismo valor contenido en , excepto cuando se actualice explícitamenteref.current
  • useEffectSolo se llama después de que el componente se renderiza con el valor anterior. Solo después de que se completa la renderización se actualiza el objeto de referencia dentro deuseEffect

Aprovechando estos dos hechos, podrás replicar fácilmente esta funcionalidad por tu cuenta.

Uso de useStatevs. useRefpara rastrear valores anteriores

Hasta ahora, este artículo se ha centrado en el uso useRefpara fines de seguimiento. Sin embargo, existe una manera de rastrear los valores anteriores usando únicamente useState. De hecho, useStatenos permite acceder al estado anterior.

Por ejemplo, utilizando el contraejemplo:

setCount (( count ) = count + 1 )  

En este ejemplo, countes nuestro estado anterior y estamos decidiendo cómo transformarlo antes de establecerlo en el recuento actual.



Como resultado, sería posible almacenar ese recuento anterior en un valor separado para fines de seguimiento:

importar { useState } de "react" ; const INITIAL_COUNT = 0 ;    función App () { /* Tener dos valores separados para el conteo y el conteo anterior */ const [ count , setCount ] = useState ( INITIAL_COUNT ); const [ previousCount , setPreviousCount ] = useState ( INITIAL_COUNT );              const setCounters = () = {     setCount (( prevState ) = { const newCount = prevState + 1 ; /* establece PreviousCount en el estado anterior del conteo, también conocido como el conteo antiguo, y luego devuelve el nuevo conteo para que el valor del conteo pueda actualizarse */       setPreviousCount ( prevState ); return newCount ; }); };                                return (  h1  Ahora : { count }, antes : { previousCount } { /* Aumentar el recuento al hacer clic */ }  br /  button onClick ={ setCounters } Aumentar el recuento / button  / h1  ); }                                     

Si bien esta solución parece más sencilla que usarla useRefpara rastrear estados, tenga mucho cuidado con useState.

React es asíncrono y utiliza actualizaciones por lotes al trabajar con componentes, por lo que debe tener cuidado al establecer el estado de una variable con otro estado. De lo contrario, podría generar un bucle infinito si actualiza previousCountalgo que también afecte al recuento.



Su uso useRefes más complejo, ya que requiere tres ganchos diferentes: useState, useRefy useEffect. La principal ventaja de useRefes que no provoca que el componente se vuelva a renderizar.

Propiedades vs. estado en React

¿Hay alguna forma de obtener las propiedades anteriores en React? Antiguamente, cuando los desarrolladores usaban componentes de clase y funciones de ciclo de vida, existía una función componentDidUpdateque permitía acceder a una propiedad antes y después de la mutación del componente. Desde entonces, React ha dejado de usar estas funciones. Por lo tanto, propslas propiedades son los valores más recientes de un componente y deben tratarse como de solo lectura dentro de ese componente.

Si un desarrollador desea mantener un historial de los valores, puede utilizar el estado como en el ejemplo anterior con county previousCount.

¿El estado y los accesorios se representan al mismo tiempo?

La respuesta es: no necesariamente. Tomemos este ejemplo. Tenemos un componente llamado Counterque acepta una propiedad y se usa para establecer el valor inicial de count:

exportar const Contador = ({conteo_inicial}) = { const [conteo, establecerConteo] = useState(conteo_inicial) devolver (div{conteo}/foo)}

En este ejemplo, incluso si las propiedades cambian, el recuento no cambia. Esto se debe a que, a pesar de que el componente se vuelve a renderizar como resultado del cambio de propiedades, useStateno se vuelve a renderizar. La mejor manera de solucionar esto sería pasar useStateal componente principal y luego pasarlo countcomo propiedad. Entonces, se mostraría el recuento actualizado.

¿Puedes conseguir un accesorio anterior con un useEffectgancho?

Si, podemos usar el usePreviousHook con un useEffectHook.

Por ejemplo, podemos crear un componente llamado Countery pasar el conteo actual como propiedad. Luego, podemos llamar usePreviouspara crear una referencia con el conteo anterior. A medida que el conteo de propiedades cambia, el usePreviousHook también se actualizará y useEffectregistrará el resultado en la consola, con un aspecto similar a este:

conteo: 1conteoPrev: 0// conteo de clics: 2conteoPrev: 1// conteo de clics: 3conteoPrev: 2// conteo de clics: 4conteoPrev: 3importar usePrevious de "./usePrevious";importar { useEffect } de "react";exportar const Counter = ({ conteo }) = { const prevCount = usePrevious(conteo); useEffect(() = { console.log("conteo: ", conteo); console.log("prevCount: ", prevCount); }, [conteo, prevCount]); devolver divcount: {conteo}/div;};

React Hooks vs. componentes de clase para la gestión de estados

Como mencionamos anteriormente, históricamente, React ofrecía numerosas funciones de ciclo de vida, como componentDidUpdate, componentDidMounty muchas más. Estas funciones eran utilizadas por los desarrolladores para gestionar el estado en las distintas etapas del ciclo de renderizado.

Los desarrolladores usarían estos métodos de ciclo de vida dentro de los componentes de clase. Por ejemplo, aquí hay un componente de clase:

importar { Componente } de 'react';clase Contador extiende Componente { render() { devolver h1Count: {this.props.count}/h1; }}

Desde entonces, React se ha distanciado de estos y ha impulsado el uso de ganchos. Esto reduce la cantidad de código escrito, a la vez que aumenta la reutilización. Los ganchos son una excelente manera de extraer y reutilizar código fácilmente.

Hoy en día, los componentes de clase y los métodos de ciclo de vida solo están disponibles en la parte heredada de React y no se recomiendan para proyectos nuevos.

Uso de Zustand para la gestión del estado

Si desea usar una biblioteca de terceros para facilitar la gestión de estados, le recomiendo la API de Zustand. Zustand le permite crear suscriptores, que son funciones que le permiten obtener los valores anteriores y actuales de sus variables de estado. Primero, cree un almacén y su suscriptor como se indica a continuación:

importar crear desde "zustand";const useStore = create((set) = ({ dólares: 0, increaseIncome: () = set((estado) = ({ dólares: estado.dollars + 1 }))}));//crear un suscriptorconst unsubscribe = useStore.subscribe((newValue, oldValue) = { console.log( "nuevo Valor " + newValue.dollars + ", antiguo Valor:" + oldValue.dollars );});

Arriba, declaramos primero una variable global llamada dollars. Su valor inicial será 0. Luego, creamos una función llamada increaseIncome. Si el usuario ejecuta este método, el programa incrementará el dollarsestado.

Luego, codificamos una función de suscriptor que registrará el valor actual y el valor anterior de la dollarsvariable. Para usar este almacén, escriba el siguiente código:

export default function App() { //extrae nuestro estado y funciones const dolares = useStore((estado) = estado.dollars); const increaseIncome = useStore((estado) = estado.increaseIncome); return ( div h1 Actual: ${dollars} /h1 {/*Al hacer clic, ejecuta la función 'increaseIncome' */} button onClick={() = increaseIncome()}Increment /button /div );

Con el código anterior, obtuvimos la dollarsvariable y la increaseIncomefunción de nuestra tienda personalizada:

Conclusión

En este artículo, aprendimos a obtener valores previos de los ganchos , , y en React. El useRefobjeto useStatedevuelto usePreviousal invocar permanece igual en todas las repeticiones de renderizado de un componente funcional, lo cual es una característica muy útil. Esto se logra sin tener que pasar ninguna dependencia de array, como en o .useEffectrefuseRefuseMemouseCallback

Cuando combinas la capacidad de usarlo useRefcomo una variable de instancia con el hecho de que el useEffectHook siempre se activa después de que se evalúa la declaración de retorno del componente padre, tienes un arma invaluable a tu alcance.

Si quieres conocer otros artículos parecidos a Cómo acceder a propiedades o estados anteriores con React Hooks puedes visitar la categoría Guias.

Entradas Relacionadas