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 useRef
y useState
el 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
, useState
y 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 :
- ¿Qué es el estado anterior?
- Obtener los accesorios o el estado anterior a través deuseRef
- Ganchos personalizados con el usePreviousgancho
- useRefEn profundidad: Un gancho de React pasado por alto
- Recuperando propiedades y estados anteriores conuseRef
- Uso de useStatevs. useRefpara rastrear valores anteriores
- Propiedades vs. estado en React
- ¿El estado y los accesorios se representan al mismo tiempo?
- ¿Puedes conseguir un accesorio anterior con un useEffectgancho?
- React Hooks vs. componentes de clase para la gestión de estados
- Uso de Zustand para la gestión del estado
- 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ñadoresDe 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, userId
no es necesario obtener la propiedad anterior, ya que la función de limpieza la capturará en un cierre. Si userId
cambia de 3
a 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 proyectoSi 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 Transition
componente. 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 Counter
componente:
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 count
estado como de costumbre mediante la función de actualización de estado. En cuanto se actualiza el estado, también actualizamos el ref
valor actual al nuevo valor del estado.
Sin embargo, debido a que ref
las 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.current
useEffect
Ganchos personalizados con el usePreviousgancho
Basándonos en la solución anterior, podemos crear una abstracción a través de un usePrevious
Hook:
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 usePrevious
funciona el Hook? Analicemos con claridad qué sucede dentro del Hook y cómo se conserva el valor anterior.
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 useRef
Hook . Además de ser excelente para gestionar referencias DOM, useRef
es 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 Count
clase tendrá su propia specialVariable
variable de instancia. El useRef
Hook 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 useRef
gancho 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"}
useRef
current
ref
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 specialVariable
objeto de referencia no se recalcula al Count
volver a renderizar el componente. Con useRef
, el valor guardado en el ref
objeto 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 ref
objeto 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 Counter
aplicación, primero se invoca el useState
Hook y se configuran las variables count
y . Tenga en cuenta que ahora es :setCount
count
0
A continuación, se ejecutará la siguiente línea. El usePrevious
gancho se invoca con el valor actual de la variable de estado de conteo 0
:
Al invocar el usePrevious
Hook, React crea una nueva useRef
instancia:
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 useEffect
Hook 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 prevCount
variable contiene el valor undefined
:
El return
valor 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}/h1
count
prevCount
0
undefined
useEffect
usePrevious
useEffect
Dentro de la effect
función tenemos lo siguiente:
useEffect(() = { ref . current = valor ; },[ valor ]);
La línea dentro de la useEffect
función actualiza la propiedad actual del ref
objeto a valor. value
ahora representa el valor inicial del Hook personalizado. En este caso, el valor es 0
. En este flujo actual, recall usePrevious
solo se ha llamado una vez con el valor inicial de 0
:
Ahora, la referencia contiene el valor 0
. Cuando la count state
variable dentro de la aplicación se actualiza de 0
a 1
o a un nuevo recuento, la aplicación ejecutará este fragmento de código de nuevo. El usePrevious
gancho se invoca con el nuevo valor de estado 1
.
Luego, se evalúa la instrucción de retorno , que devuelve [ sub ( …ref.current
0
1
ref.current
useEffect
0
return
Solo después del renderizado, la useEffect
llamada dentro del usePrevious
Hook se actualiza con el nuevo valor. 1
Este 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
ref
objeto siempre devolverá el mismo valor contenido en , excepto cuando se actualice explícitamenteref.current
useEffect
Solo 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 useRef
para fines de seguimiento. Sin embargo, existe una manera de rastrear los valores anteriores usando únicamente useState
. De hecho, useState
nos permite acceder al estado anterior.
Por ejemplo, utilizando el contraejemplo:
setCount (( count ) = count + 1 )
En este ejemplo, count
es 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 useRef
para 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 previousCount
algo que también afecte al recuento.
Su uso useRef
es más complejo, ya que requiere tres ganchos diferentes: useState
, useRef
y useEffect
. La principal ventaja de useRef
es 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 componentDidUpdate
que 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, props
las 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 count
y previousCount
.
¿El estado y los accesorios se representan al mismo tiempo?
La respuesta es: no necesariamente. Tomemos este ejemplo. Tenemos un componente llamado Counter
que 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, useState
no se vuelve a renderizar. La mejor manera de solucionar esto sería pasar useState
al componente principal y luego pasarlo count
como propiedad. Entonces, se mostraría el recuento actualizado.
¿Puedes conseguir un accesorio anterior con un useEffectgancho?
Si, podemos usar el usePrevious
Hook con un useEffect
Hook.
Por ejemplo, podemos crear un componente llamado Counter
y pasar el conteo actual como propiedad. Luego, podemos llamar usePrevious
para crear una referencia con el conteo anterior. A medida que el conteo de propiedades cambia, el usePrevious
Hook también se actualizará y useEffect
registrará 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
, componentDidMount
y 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 dollars
estado.
Luego, codificamos una función de suscriptor que registrará el valor actual y el valor anterior de la dollars
variable. 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 dollars
variable y la increaseIncome
función de nuestra tienda personalizada:
Conclusión
En este artículo, aprendimos a obtener valores previos de los ganchos , , y en React. El useRef
objeto useState
devuelto usePrevious
al 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 .useEffect
ref
useRef
useMemo
useCallback
Cuando combinas la capacidad de usarlo useRef
como una variable de instancia con el hecho de que el useEffect
Hook 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