Inicio / Artículos

Reconsiderando el estado de React: dónde realmente deberían residir sus datos

Menos errores en React gracias a menos uso de useState: coloca el estado en la URL, en el DOM o en valores derivados, y decide quién es responsable de cada parte antes de recurrir a la gestión de estado.

Editor: REACTAPP.TOP17 min de lectura3931 palabras

Fuentes: Stop Adding React State: It’s Creating Bugs You Don’t Need

Un representante de ventas en una empresa SaaS en crecimiento pasa buena parte de la mañana revisando una base de datos de clientes que contiene diez mil cuentas.

Tras un filtrado cuidadoso, identifica a los clientes empresariales con sede en Europa cuyas suscripciones no se han renovado en los últimos noventa días.

El resultado es una lista precisa de treinta cuentas que requieren atención inmediata.

Toma la URL directamente del navegador y la envía por Slack a su director regional junto con una nota:

"Aquí están las cuentas prioritarias de esta mañana."

El director abre el enlace.

La página se carga.

Pero en lugar de la lista filtrada, muestra la vista por defecto: todas las diez mil cuentas, sin filtrar y sin ordenar.

Toda esa filtración cuidadosa desapareció porque el ingeniero que creó la página había almacenado cada filtro, campo de ordenación y valor de paginación dentro del estado del componente local utilizando useState.

Este escenario ilustra un patrón que aparece constantemente en los códigos de React.

Tendemos a recurrir al estado simplemente porque React lo hace tan fácil de crear.

Necesita un valor de filtro?

useState.

Necesita rastrear qué pestaña está activa?

useState.

Necesita un contador?

useState.

Necesita conservar un campo de formulario?

useState.

Necesita rastrear las diferentes fases de una llamada a API?

Eso son tres llamadas más a useState.

En poco tiempo, un único componente maneja docenas de valores que en realidad nunca necesitaron estar en el estado de React desde un principio.

El estado en sí mismo no es el enemigo aquí. Cada aplicación React compleja necesita algo de estado.

El verdadero problema es colocar el estado en un lugar donde no debería estar.

Cada variable de estado adicional es algo más con lo que tu aplicación debe mantenerse sincronizada con todo lo demás.

Si no se controla, esto tiende a causar una serie de problemas, entre ellos:

  • Trabajo adicional para mantener alineados los valores duplicados
  • Datos que se vuelven obsoletos en algún caché
  • Valores mostrados en la pantalla que ya no coinciden con la realidad
  • Renderizaciones que se ejecutan cuando en realidad no ha cambiado nada significativo
  • Errores de sincronización en los que dos actualizaciones chocan de forma impredecible
  • Efectos que se vuelven cada vez más complicados al intentar mantener todo consistente
  • Errores que requieren mucho más tiempo para rastrear su origen
  • Una página que olvida todo en el momento en que se vuelve a cargar
  • Pantallas a las que nadie puede hacer enlaces o guardar para más tarde
  • Combinaciones de flags que generan un estado de interfaz de usuario que nunca debería existir
  • Escribir código React excelente no se trata realmente de dominar la lógica compleja de estados.

    A menudo, se trata de reconocer cuándo no crear ese estado en absoluto.

    1. La URL es el mejor gestor de estados de tu aplicación

    Imagina una pantalla típica de una aplicación con muchos datos.

    Probablemente necesite rastrear:

    • Búsqueda
    • Filtros
    • Ordenamiento
    • Paginación
    • Pestañas activas
    • Categorías seleccionadas
    • Rangos de fechas

    El enfoque habitual es almacenar todo esto en el estado del componente.

    function ProductList() {
      const [activeCategory, setActiveCategory] = useState('all');
      const [currentPage, setCurrentPage] = useState(1);
      const [searchQuery, setSearchQuery] = useState('');
    
      // Fetch and filter data...
    }
    

    A primera vista, esto parece perfectamente bien.

    Luego el usuario pulsa actualizar.

    Todo valor vuelve a su valor por defecto.

    O comparten el enlace con un colega.

    Ese colega accede a una vista completamente diferente.

    O hacen clic en el botón Atrás del navegador.

    El filtrado que habían configurado podría no volver a aplicarse, ya que el estado de React no tiene una conexión integrada con el historial de navegación del navegador.

    useState en sí no es el problema aquí.

    El verdadero problema es que la URL ya existe como mecanismo para representar el estado de navegación, y se está ignorando por completo.

    En lugar de escribir:

    const [currentPage, setCurrentPage] = useState(1);
    const [searchQuery, setSearchQuery] = useState('');
    const [category, setCategory] = useState('all');
    

    puedes codificar esa misma información directamente en la URL.

    Algo como:

    /products?category=enterprise&page=3&query=react
    

    Al usar una biblioteca de enrutamiento como Next.js, React Router o TanStack Router, puedes leer esos parámetros directamente desde la URL:

    import { useSearchParams } from 'next/navigation';
    
    function ProductList() {
      const searchParams = useSearchParams();  const category = searchParams.get('category') || 'all';
      const page = Number(searchParams.get('page')) || 1;
      const query = searchParams.get('query') || '';  // Fetch data using the URL state...
    }
    

    Este pequeño cambio modifica drásticamente el ciclo de vida de ese estado.

    ¿Actualizar la página?

    Los filtros permanecen intactos.

    ¿Compartir el enlace?

    Quien lo abra verá exactamente la misma vista.

    ¿Guardarlo como marcador?

    Se cargará nuevamente en ese mismo estado más tarde.

    ¿Ir atrás?

    El navegador navega correctamente por los estados anteriores.

    Eso representa una ventaja arquitectónica significativa.

    El estado de la URL ofrece más que solo persistencia

    Sobrevivir a una actualización de página es solo parte de las ventajas.

    La mayor ventaja es que la URL se convierte en una instantánea portátil del estado actual de tu aplicación que puede compartirse con cualquiera.

    Tomemos como ejemplo un panel de control de soporte al cliente:

    /support/tickets?status=open&priority=high&team=billing
    

    Un gerente podría enviar ese enlace exacto a otro gerente al instante.

    Un panel de control orientado a ventas podría verse así:

    /accounts?region=europe&plan=enterprise&renewal=90
    

    Una herramienta de análisis podría utilizar algo similar:

    /analytics?range=30d&metric=revenue&groupBy=region
    

    Ninguna de estas es una cadena de consulta arbitraria.

    Cada una es un enlace profundo que apunta a un estado específico y reconstruible de la aplicación.

    Ese es precisamente el propósito para el cual se crearon las URLs.

    Cuándo tiene sentido usar el estado de la URL

    Los parámetros de URL resultan ser ideales para cosas como:

    • Lo que el usuario escribió en un cuadro de búsqueda
    • Las selecciones de filtro activas
    • La columna y la dirección por las que está ordenado el contenido
    • Qué página de resultados se muestra
    • La pestaña seleccionada actualmente
    • Un rango de fechas elegido
    • La vista guardada que está abierta en este momento
    • Los ajustes de un informe generado
    • La configuración personalizada en un panel de control
    • Cualquier estado de la interfaz que el usuario quiera transferir a otra persona

    Dicho esto, no todo encaja allí.

    Obviamente, las contraseñas no deberían incluirse en la URL.

    Tampoco debería ser el caso con un estado de animación modal temporal.

    Tampoco, en general, con un objeto grande y profundamente anidado.

    La pregunta guía es sencilla:

    Pregúntate si un usuario esperaría razonablemente que esta vista sea compartible, guardable como favorito o restaurable; si es así, la URL probablemente sea el lugar adecuado para ese estado.

    2. Deja de almacenar valores que podrías calcular fácilmente

    Una forma rápida de reducir el estado en React es dejar de persistir valores que en realidad se derivan de otros valores.

    Toma este ejemplo:

    const [items, setItems] = useState([]);
    const [itemCount, setItemCount] = useState(0);
    

    Y en otra parte del componente:

    setItems(newItems);
    setItemCount(newItems.length);
    

    Ahora tienes dos variables de estado separadas que describen el mismo hecho subyacente.

    Si items se actualiza pero itemCount no lo hace, tu interfaz mostrará información incorrecta.

    Han creado un problema de sincronización que nunca debería haber existido.

    Una versión más limpia sería esta:

    const [items, setItems] = useState([]);
    
    const itemCount = items.length;
    

    Ahora existe una única fuente de verdad.

    El recuento se calcula cada vez que se renderiza el componente.

    Esta misma lógica se aplica al filtrar colecciones.

    En lugar de escribir:

    const [items, setItems] = useState([]);
    const [activeItems, setActiveItems] = useState([]);
    

    por lo general basta con escribir:

    const activeItems = items.filter(item => item.active);
    

    O bien:

    const totalPrice = items.reduce(
      (total, item) => total + item.price,
      0
    );
    

    O también:

    const hasErrors = fields.some(field => field.error);
    

    O además:

    const completedCount = todos.filter(todo => todo.completed).length;
    

    La pregunta que realmente cabe hacer no es:

    "¿Podría técnicamente almacenar esto en el estado?"

    Sí, se podría hacer.

    Lo que realmente debería preguntarse es:

    "¿Este valor tiene su propio ciclo de vida independiente?"

    Si no es así, probablemente no debería formar parte del estado.

    3. Deje de vincular cada campo del formulario al estado

    Otra costumbre que vale la pena abandonar es tratar cada campo del formulario como una variable de estado independiente.

    Un formulario pequeño construido de esta manera podría verse así:

    function ContactForm() {
      const [firstName, setFirstName] = useState('');
      const [lastName, setLastName] = useState('');
      const [email, setEmail] = useState('');
      const [message, setMessage] = useState('');
    
      const handleSubmit = (e: React.FormEvent) => {
        e.preventDefault();    submitData({
          firstName,
          lastName,
          email,
          message
        });
      };  return (
        <form onSubmit={handleSubmit}>
          <input
            value={firstName}
            onChange={e => setFirstName(e.target.value)}
          />      <input
            value={lastName}
            onChange={e => setLastName(e.target.value)}
          />      <input
            value={email}
            onChange={e => setEmail(e.target.value)}
          />      <textarea
            value={message}
            onChange={e => setMessage(e.target.value)}
          />      <button type="submit">Send</button>
        </form>
      );
    }
    

    Técnicamente no hay nada incorrecto aquí.

    Los campos controlados son útiles cuando su aplicación realmente necesita reaccionar a medida que el usuario escribe. Algunas situaciones en las que esto es importante:

    • Verificar la validez de un campo mientras se escribe
    • Ofrecer sugerencias al estilo de autocompletado
    • Reformatear el texto en tiempo real
    • Contar los caracteres restantes
    • Mostrar u ocultar otros campos según la entrada
    • Evaluar cuán fuerte es una contraseña
    • Vincular el comportamiento de un campo al valor de otro

    Pero con frecuencia su aplicación no tiene razón para preocuparse por lo que escribió el usuario hasta que presiona Enviar.

    En esos casos, no es necesario que React supervise cada carácter a medida que se escribe.

    El navegador ya se encarga de esa tarea.

    Deja que el navegador se ocupe

    Los formularios HTML nativos han gestionado los valores de entrada durante décadas.

    Puedes aprovechar este comportamiento integrado con FormData:

    function ContactForm() {
      const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
        e.preventDefault();
    
        const formData = new FormData(e.currentTarget);    const payload = {
          firstName: formData.get('firstName') as string,
          lastName: formData.get('lastName') as string,
          email: formData.get('email') as string,
          message: formData.get('message') as string,
        };    submitData(payload);
      };  return (
        <form onSubmit={handleSubmit}>
          <input name="firstName" required />
          <input name="lastName" required />
          <input name="email" type="email" required />
          <textarea name="message" required />      <button type="submit">Send</button>
        </form>
      );
    }
    

    Con este patrón, React ya no necesita un espacio de estado dedicado para cada campo.

    El navegador conserva lo que ingresó el usuario.

    React simplemente lee los valores una vez que se envía el formulario.

    Esto resulta especialmente útil para formularios grandes.

    Imagina un flujo de onboarding empresarial con 30 campos.

    ¿Realmente querrías 30 variables de estado separadas solo para almacenar lo que alguien escribió?

    Probablemente no.

    Los inputs controlados y no controlados pueden coexistir

    Nada de esto significa que los componentes controlados sean un error.

    No lo son.

    Existen muchas situaciones en las que controlar la entrada es exactamente lo adecuado.

    El punto principal es simplemente abandonar la suposición de que React debe gestionar por defecto el valor de cada campo.

    Si tu aplicación no necesita los datos hasta el momento de enviarlos, a menudo basta con dejar que el formulario nativo los almacene.

    Hacer esa distinción puede simplificar considerablemente formularios extensos.

    4. Por qué las banderas booleanas fallan

    Otro problema recurrente aparece constantemente en bases de código del mundo real.

    Considera una llamada típica a la API.

    Un patrón común es rastrearla con tres booleanos separados:

    const [isLoading, setIsLoading] = useState(false);
    const [isSuccess, setIsSuccess] = useState(false);
    const [isError, setIsError] = useState(false);
    

    Al principio, los tres son falsos:

    loading = false
    success = false
    error = false
    

    Una vez que comienza la solicitud:

    loading = true
    

    Una vez que finaliza con éxito:

    loading = false
    success = true
    

    A primera vista, esto parece razonable.

    Pero supongamos que una rama de manejo de errores no logra restablecer isLoading.

    Podrías terminar con:

    loading = true
    error = true
    

    O un mecanismo de intentos descuidado podría dejarte con:

    success = true
    error = true
    

    En ese punto, tu componente se encuentra en un estado que lógicamente no tiene sentido.

    ¿Debería mostrar el mensaje de éxito?

    ¿Debería mostrar el estado de error?

    ¿Debería seguir mostrando un spinner?

    Técnicamente, las tres variables booleanas podrían justificar una respuesta de “sí” al mismo tiempo.

    Esa ambigüedad es exactamente el error.

    Trátalo como una máquina de estados finitos en su lugar

    Un enfoque más limpio:

    type RequestStatus =
      | 'idle'
      | 'loading'
      | 'success'
      | 'error';
    
    const [status, setStatus] =
      useState<RequestStatus>('idle');
    

    Ahora cada estado válido es explícito y mutuamente exclusivo.

    La solicitud solo puede ser uno de:

    idle
    loading
    success
    error
    

    No hay forma de representar accidentalmente “cargando” y “con éxito” al mismo tiempo.

    El proceso de renderizado también se vuelve mucho más sencillo de seguir:

    if (status === 'loading') {
      return <Spinner />;
    }
    
    if (status === 'error') {
      return <ErrorMessage />;
    }if (status === 'success') {
      return <SuccessBanner />;
    }return <DefaultForm />;
    

    Esto no es solo una ventaja adicional de TypeScript.

    Refleja una regla de diseño más amplia:

    Cuando dos valores de estado nunca pueden ser verdaderos de forma independiente, no deberían modelarse como dos variables separadas.

    5. Evite usar efectos para mantener el estado sincronizado

    Este es otro caso en el que las variables de estado adicionales generan código innecesario y evitable.

    Tome esta configuración:

    const [firstName, setFirstName] = useState('');
    const [lastName, setLastName] = useState('');
    const [fullName, setFullName] = useState('');
    

    Junto con:

    useEffect(() => {
      setFullName(`${firstName} ${lastName}`);
    }, [firstName, lastName]);
    

    Lo que ha creado aquí es un mecanismo de sincronización para un valor que en realidad nunca necesitó tener su propio estado.

    La solución más sencilla:

    const fullName = `${firstName} ${lastName}`;
    

    El cambio puede parecer menor, pero su impacto arquitectónico es significativo.

    La versión original necesita:

    • Tres variables de estado separadas
  • Un efecto para mantenerlos alineados
  • Una relación de sincronización implícita entre los valores
  • Un ciclo de renderizado adicional desencadenado por el efecto
  • Un array de dependencias que debe mantenerse preciso
  • La versión revisada solo necesita:

    • Dos valores reales
    • Un valor derivado de ellos
    • Ningún efecto
    • Ninguna lógica de sincronización que mantener

    Un buen hábito al revisar componentes de React es preguntarse:

    "¿Por qué este valor necesita existir fuera del cálculo de renderizado?"

    Si la respuesta honesta es "para mantenerse sincronizado con algo más," eso suele ser una señal de que el valor en realidad no debería ser estado.

    6. Tres preguntas que hacer antes de agregar estado

    Antes de escribir esta línea:

    const [value, setValue] = useState(...);
    

    Primero, realice tres verificaciones.

    Pregunta 1: ¿Se puede calcular esto mientras se renderiza?

    Si los datos necesarios para generar este valor ya existen, no hay razón para almacenar una copia duplicada.

    En lugar de:

    const [itemCount, setItemCount] = useState(0);
    

    escribir:

    const itemCount = items.length;
    

    En lugar de:

    const [hasErrors, setHasErrors] = useState(false);
    

    escribir:

    const hasErrors = fields.some(field => field.error);
    

    Evite almacenar el estado de cualquier cosa que pueda obtenerse directamente en el momento.

    Pregunta 2: ¿Debería sobrevivir a la recarga de la página?

    Si así debe ser, considere si la URL es el lugar adecuado para almacenarla.

    Candidatos típicos:

    • Términos de búsqueda
    • Estatuto de paginación
    • Filtros activos
    • Orden de clasificación
    • Pestañas seleccionadas
    • Rangos de fechas

    Pregúntese: si se comparte un enlace a esta página, ¿debería el destinatario ver exactamente la misma vista?

    Si es así, por lo general la URL es el lugar más adecuado para esos datos.

    Pregunta 3: ¿Puede el navegador conservar esto hasta que se envíe el formulario?

    Si el valor de un campo solo es relevante en el momento del envío, puede que lo único que necesites sea una entrada no controlada.

    En lugar de:

    const [email, setEmail] = useState('');
    

    puede que solo necesites:

    <input name="email" type="email" />
    

    y leerlo más tarde con:

    const formData = new FormData(e.currentTarget);
    

    Deja que el navegador se encargue de lo que ya está diseñado para gestionar.

    7. Qué realmente pertenece al estado de React

    Nada de esto es un argumento para eliminar por completo el estado de los componentes.

    Tratar de eliminarlo totalmente sería una corrección excesiva.

    Muchos valores son adecuados para estar en el estado de forma legítima.

    Por ejemplo:

    const [isOpen, setIsOpen] = useState(false);
    

    Un menú desplegable temporal o una ventana modal no necesitan reflejarse en la URL en absoluto.

    Estado interactivo

    const [activeStep, setActiveStep] = useState(1);
    

    Cuando un valor controla una interacción que permanece completamente dentro de un único componente, mantenerlo en el estado local suele ser la decisión correcta.

    Valores que cambian de forma independiente

    Si algo realmente tiene su propio ciclo de vida y no hay forma de calcularlo a partir de los datos que ya se poseen, entonces el estado es el lugar adecuado para ello.

    UI optimista

    Ocasionalmente es necesario mostrar algo en la pantalla antes de que el servidor confirme realmente que ocurrió.

    Eso también constituye un uso legítimo del estado.

    El objetivo no es seguir esta regla:

    "Usa menos useState sin importar nada más."

    En cambio, el objetivo es este:

    "Asegúrate de que cada elemento de estado tenga su dueño adecuado."

    A veces el dueño adecuado es React.

    Otras veces es la URL.

    A veces es el DOM.

    Otras veces es el servidor.

    A veces es una capa dedicada a la obtención de datos o al manejo del estado.

    Esa decisión sobre quién es el propietario es lo que realmente da forma a tu arquitectura.

    8. Concéntrate en quién posee el estado, no solo en cuánto tienes

    Una aplicación React bien construida no se define por tener un número reducido de variables de estado.

    Lo que realmente importa es asignar a cada parte del estado un propietario claro y correcto.

    Toma un panel de control que registra:

    Search query
    Filters
    Pagination
    Sort order
    Form values
    Server data
    Loading status
    Selected row
    Modal visibility
    Derived totals
    

    Es tentador incluir cada uno de estos elementos en useState sin pensarlo dos veces.

    Pero estos valores no tienen la misma función, por lo que no deberían compartir el mismo lugar.

    Un modelo mental más útil los divide de la siguiente manera:

    URL

    • Búsqueda
    • Filtros
    • Paginación
    • Orden de clasificación
    • Pestañas compartibles

    DOM

    • Valores de formulario que solo son relevantes una vez enviado el formulario

    Servidor / Capa de datos

    • Registros de clientes
    • Respuestas de la API
    • Datos del servidor en caché

    Valores derivados

    • Cuentas
    • Totalidades
    • Listas filtradas
    • Resúmenes de validación

    Estatuto de React

    • Visibilidad de modales
    • Selecciones temporales
    • Estatuto de animaciones
    • Interacciones de corta duración

    Cuando distribuyes el estado de esta manera, el componente tiene que mantener sincronizado mucho menos contenido.

    Esa reducción es donde radica el verdadero beneficio.

    9. El estado duplicado es la causa de los errores del futuro

    El estado más arriesgado en una aplicación no es necesariamente el más complejo.

    A menudo es el estado que existe en más de un lugar al mismo tiempo.

    Imagina que tienes:

    const [products, setProducts] = useState([]);
    const [filteredProducts, setFilteredProducts] = useState([]);
    

    Ahora le toca a usted asegurarse de que filteredProducts siempre coincida con lo que hay en products.

    Agregue otro filtro y obtendrá:

    const [filteredProducts, setFilteredProducts] = useState([]);
    const [searchResults, setSearchResults] = useState([]);
    

    Ahora existe una relación adicional que debe mantenerse consistente manualmente.

    Tarde o temprano, alguien actualiza los datos subyacentes pero olvida actualizar una de las variables derivadas que dependen de ellos.

    La interfaz deja de estar sincronizada con la realidad sin que se note.

    Eventualmente, un usuario reportará algo como:

    "La cuenta indica 42 productos, pero solo se muestran 39 en la pantalla."

    Esa discrepancia generalmente no es un defecto en la lógica de filtrado en sí.

    Es un síntoma de que la aplicación tiene más de una fuente de verdad para la misma información.

    Si en cambio deriva el valor:

    const filteredProducts = products.filter(...);
    

    Se elimina toda una categoría de errores antes de que puedan ocurrir.

    Por eso, reducir el estado no se trata únicamente del rendimiento de la renderización.

    En realidad se trata de disminuir el número de elementos que pueden desincronizarse.

    10. El costo del estado va más allá de las re-renderizaciones

    Cuando la gente habla del estado, la conversación suele centrarse en el rendimiento: “¿esto provocará otra renderización?”

    Esa es una preocupación válida, pero no es la parte más costosa del problema.

    El verdadero costo es la complejidad.

    Cada variable de estado adicional es algo más que su equipo debe tener en cuenta.

    Luego, inevitablemente, se añade una llamada a un setter.

    setSomething(...)
    

    Luego, probablemente, un useEffect para reaccionar ante ello.

    useEffect(...)
    

    Luego, un array de dependencias que enumera los valores que monitorea.

    [firstValue, secondValue]
    

    Luego, algún tipo de lógica de reinicio para devolverlo a su valor inicial.

    setSomething(initialValue)
    

    Luego, un caso límite en el que ese estado debe mantenerse alineado con un cambio de navegación.

    Luego, un informe de error porque el estado no se limpió después de alguna mutación.

    Poco a poco, la base de código se convierte en una red de lógica de sincronización.

    Eso es considerablemente más difícil de comprender que un componente cuyos datos fluyen de manera ordenada desde unas pocas fuentes bien definidas.

    La verdadera habilidad en React no es gestionar más estado

    Sino entender dónde pertenece cada pieza de estado.

    Un desarrollador menos experimentado al observar una interfaz de usuario podría preguntarse:

    "¿Qué estado necesita esto?"

    Un instinto arquitectónico mejor lleva a una pregunta diferente:

    “¿Cuáles de estos valores realmente necesitan estar en el estado?”

    Y la versión más precisa de la pregunta es:

    “¿Quién es el propietario legítimo de cada parte del estado?”

    Cambiar su forma de pensar de esta manera puede modificar por completo la manera en que aborda la creación de aplicaciones React.

    La URL puede ser responsable del estado de navegación.

    El navegador puede encargarse del estado de los formularios.

    El servidor puede gestionar los datos que le pertenecen a él.

    Los valores derivados pueden permanecer como tales en lugar de ser duplicados.

    Las uniones de TypeScript pueden modelar estados que son mutuamente excluyentes.

    Y React mismo puede concentrarse en el estado interactivo de corta duración que realmente pertenece a la capa de interfaz de usuario.

    Esa división del trabajo genera una arquitectura mucho más limpia.

    Resumen

    Tener más estados no es un logro en sí mismo.

    Más variables de estado no hacen que una aplicación sea más capaz.

    Por lo general, solo significa más trabajo de sincronización, más dependencias que seguir y más posibilidades de que los datos se vuelvan inconsistentes.

    Antes de usar otro useState, responda a estas preguntas:

    1. ¿Se puede calcular este valor durante la renderización? Si es así, obténgalo en lugar de almacenarlo por separado.
    2. ¿Es necesario que sobreviva a un recargo de página o pueda compartirse mediante un enlace? Si es así, guárdelo en los parámetros de búsqueda de la URL.
    3. ¿Es necesario rastrear este campo de formulario con cada tecla pulsada? Si no, un input sin control junto con FormData podría ser suficiente.
  • ¿Varias banderas booleanas describen en realidad un único ciclo de vida subyacente? Agrúgalas en un único valor de estado o máquina de estados.
  • ¿Se crea este estado solo para mantenerlo sincronizado con otro estado? Si es así, probablemente estés almacenando algo que podrías obtener directamente.
  • ¿Este valor realmente tiene su propio ciclo de vida? Si no, es probable que ni siquiera necesite ser un estado.
  • Nunca fue el objetivo minimizar la cantidad bruta de llamadas a useState.

    Se trata de asegurarse de que el menor número posible de valores sea responsable de definir la lógica de la aplicación.

    Porque cada estado innecesario es algo más que tu aplicación debe mantener sincronizado.

    Y cada paso de sincronización que evitas escribir es un error que nunca tendrás que corregir.

    Lecturas relacionadas