79

useState vs. useReducer: Cómo evitar el código espagueti - #ReactHooks

211925Puntos

hace 4 años

React.useState NO es la única herramienta para manejar el estado en React. Estudiarlas a profundidad te ayudará a descifrar cuándo vale la pena usar cada una.

En este tutorial vamos a estudiar cómo aplicar la programación declarativa al manejo del estado con React Hooks.

Tanto useState como useReducer tienen el mismo objetivo: manejar el estado local de nuestros componentes. La diferencia está en el camino que nos ofrecen para llegar a una solución. No hay ganadores, simplemente hay herramientas y paradigmas que se adaptan mejor a nuestros objetivos.

Paradigma > Herramienta

La palabra paradigma significa forma de pensar. Los paradigmas de programación son formas de pensar para resolver un problema con código. Entender que NO hay formas de pensar correctas o incorrectas es fundamental para crecer profesionalmente y convertirte en la mejor desarrolladora que puedes ser.

Hay paradigmas que se adaptan muy bien a diferentes situaciones, así que usarlos nos da cierta facilidad para resolver cierto tipo de problemas. Saber cuándo usar un paradigma u otro nos hace muy eficientes para resolver todo tipo de problemas. Pero cerrarnos a una sola forma de pensar nos hará mil veces menos eficientes.

Teniendo esto claro, vamos a estudiar los diferentes paradigmas que representan los React Hooks que manejan el estado de nuestros componentes: useState y useReducer.

LET IT BEGIN!!!

Proyecto

Nuestro proyecto será una caja de confirmación con los siguientes requerimientos:

  • Código de seguridad: el usuario debe escribir la contraseña “secreta” para confirmar que quiere realizar la eliminación.
  • Botón de comprobar, mensajes de cargando y error: cuando el usuario escriba la contraseña debe darle clic al botón de comprobar el código. Este botón simulara una solicitud a alguna API. Mientras carga, debemos mostrar un mensaje de “cargando”. Y si el código es incorrecto, debemos pedirle al usuario que lo intente de nuevo.
  • Ultra confirmación: si el código es correcto, dejamos de mostrar el campo donde el usuario escribe la contraseña y le preguntamos si está seguro de que quiere realizar la eliminación. El usuario tiene la opción de cancelar y volver a la pantalla anterior. O también puede confirmar definitivamente.
  • Eliminado: cuando el usuario haya pasado por todo este proceso debe poder recuperar el estado anterior haciendo clic en un botón que diga “¡me arrepiento, devuelvanme al estado anterior!”.

Aquí puedes ver el demo de la aplicación: juandc.co/usestate-vs-usereducer/. El código de seguridad es “paradigma”. 😉

Interfaz (UI)

Primero nos encargaremos de la UI. Luego añadiremos la lógica y el estado a nuestro componente.

  1. Si el usuario no ha realizado la eliminación, entonces debemos ver todo el sistema para realizarla. Si no, debemos ver el mensaje de que hemos terminado y la opción de recuperar el estado anterior.
<>
  {isActive && (
    {/* … */}
  )}

  {!isActive && (
    {/* … */}
  )}
</>
  1. El usuario verá una UI similar mientras haya o no escrito la contraseña correcta.
{isActive && (
  <divclassName="ActiveBox"><h2className="Box-title">Eliminar UseState</h2>

    {!isPasswordCorrect && (
      {/* … */}
    )}

    {isPasswordCorrect && (
       {/* … */}
    )}
  </div>
)}
  1. Si el usuario NO ha escrito la contraseña debemos ver un input para escribir, un botón para comprobar que la contraseña sea correcta y mensajes de cargando o error dependiendo de lo que escriba o haga el usuario.
{!isPasswordCorrect && (
  <>
    <p>
      Por favor, escribe el código de seguridad para comprobar que quieres eliminar.
    </p>

    {isLoading && <p>Cargando...</p>}
    {hasError && <p>Error...</p>}

    <input placeholder="Código de Seguridad" />

    <button onClick={/* … */}>Comprobar</button>
  </>
)}
  1. El botón de comprobar la contraseña es el que tiene más responsabilidades. Si la contraseña es correcta debemos pasar a la siguiente pantalla. Si aún estamos esperando una respuesta de la API (que por ahora es falsa) debemos activar el mensaje de cargando. Y si el código es incorrecto debemos activar el mensaje de error.
<button onClick={/*
  - si la contraseña es correcta, `isPasswordCorrect` debe
    volverse false
  - si estamos esperando una respuesta de la API, `isLoading` debe
    volverse true
  - si la API indica que el código es incorrecto, `hasError` debe
    volverse false
*/}>
  1. Si YA se escribió la contraseña correcta debemos mostrar dos botones de confirmación: uno para cancelar y otro para confirmar la eliminación.
{isPasswordCorrect && (
  <><p>¿Seguro que quieres eliminar?</p><buttononClick={/* isActivedebevolversetrue */}>
      Sí, eliminar
    </button><buttononClick={/*
      debemosvolveralestadoinicial, elusuariodebevolveraescribirlacontraseñaynohaymensajesdeerrorocargando
    */}>
      No, volver
    </button></>
)}
  1. Si el usuario YA realizó la eliminación debemos crear un botón que le permita cancelar.
{!isActive && (
  <div><h2>Eliminación exitosa</h2><buttononClick={/* isActivedebevolversetrue */}>
       Recuperar, cancelar la eliminación
    </button></div>
)}

Puedes ver el código completo (hasta el momento) aquí: github.com/juandc/usestate-vs-usereducer/blob/master/ui.js.

A partir de aquí debemos construir la lógica de nuestro componente. Vamos a hacerlo primero con useState y luego con useReducer.

Lógica con useState (y un poquito vs. Clases)

Los componentes creados como clases solo tienen un estado, el que guardamos en this.state. Pero useState funciona un poco diferente. Podemos crear todos los estados independientes que necesitemos.

Veamos un ejemplo con el input donde el usuario va a escribir el código de seguridad:

// Con clases:classComponentextendsReact.Component{
  state = { confirmationCode: '' };

  setConfirmationCode = (e) => {
    this.setState({
      confirmationCode: e.currentTarget.value,
    });
  }

  render {
    return (
      <>
        {/* ... */}

        <input
          value={this.state.confirmationCode}
          onChange{this.setConfirmationCode}
        />

        {/* ... */}
      </>
    );
  }
}

// Con Hooks:
function Component(props) {
  const [confirmationCode, setConfirmationCode] = useState('');

  const setConfirmationCodeOnChange = (e) => {
    setConfirmationCode(e.currentTarget.value);
  }
  
  return (
    <>
      {/* ... */}

      <input
        value={confirmationCode}
        onChange{setConfirmationCodeOnChange}
      />

      {/* ... */}
    </>
  );
}

Ahora añadamos los controladores de los mensajes de error y cargando:

// Con clases:classComponentextendsReact.Component{
  state = {
    confirmationCode: '',
    isLoading: false,
    hasError: false,
  };

  setConfirmationCode = (e) => {/* … */}

  toggleLoading = (newIsLoading) => {
    this.setState({ isLoading: newIsLoading });
  }

  toggleError = (newError) => {
    this.setState({ hasError: newError });
  }

  render {
    const {
      confirmationCode,
      isLoading,
      hasError,
    } = this.state;

    return (
      <>
        {/* ... */}

        {isLoading && <p>Cargando...</p>}
        {hasError && <p>Error...</p>}

        <input
          value={confirmationCode}
          onChange{this.setConfirmationCode}
        />

        {/* ... */}
      </>
    );
  }
}

// Con Hooks:
function Component(props) {
  const [confirmationCode, setConfirmationCode] = useState('');
  const [isLoading, setIsLoading] = useState(false);
  const [hasError, setHasError] = useState(false);

  const setConfirmationCodeOnChange = (e) => {
    setConfirmationCode(e.currentTarget.value);
  };
  
  return (
    <>
      {/* ... */}

      {isLoading && <p>Cargando...</p>}
      {hasError && <p>Error...</p>}

      <input
        value={confirmationCode}
        onChange{setConfirmationCodeOnChange}
      />

      {/* ... */}
    </>
  );
}

Cuando usamos Clases debemos entender el estado como un solo objeto con todo el estado del componente. En cambio, al usar Hooks podemos manejar independientemente cada elemento de nuestro componente por defecto.

Vamos a dejar hasta aquí la comparación entre Clases y Hooks. Solo recuerda que useState nos permite separar la lógica de cada elemento de nuestro componente, algo que en muchos casos puede resultar bastante cómodo.

👉 ¿Sabías que compilar un componente de tipo clase es más trabajo para Babel?
👉 ¿Cuándo crear un Componente? Estructura, Organización y Tipos de Componentes en React

Sigamos construyendo nuestro componente. Recuerda que nuestra UI es muy interactiva, así que debemos crear la lógica de muchos más controladores.

Formularios con useState

Continuemos con el botón encargado de comprobar que el código que ha escrito el usuario sea correcto. Para esto necesitamos un nuevo estado que nos indique si el usuario ha escrito bien la contraseña, lo llamaremos isPasswordCorrect. Y también vamos a crear una función para manejar la lógica de este botón, la llamaremos confirmPasswordOnClick.

¡Hola! Vengo del pasado para recordarte que si la contraseña es correcta, isPasswordCorrect debe volverse false; si estamos esperando una respuesta de la API (o lo que se supone que debería ser una API), isLoading debe volverse true; y si la API indica que el código es incorrecto, hasError debe volverse false.

Recuerda que esta función debe ser asíncrona, ya que (en teoría) aquí llamamos una API. Y antes de la comprobación debemos dejar al componente en estado de carga.

functionComponent(props) {
  const [confirmationCode, setConfirmationCode] = useState('');
  const [isLoading, setIsLoading] = useState(false);
  const [hasError, setHasError] = useState(false);
  const [isPasswordCorrect, setIsPasswordCorrect] = useState(false);

  const setConfirmationCodeOnChange = (e) => {/* … */};

  const confirmPasswordOnClick = async () => {
    // Empezamos cargando. Aún no mostramos si hay errores,// apenas vamos a comprobar si la clave es correcta:
    setIsLoading(true);
    setHasError(false);

    // `sampleFetch` debería ser la petición a la API,// yo voy a usar la funcion “sleep”:// https://flaviocopes.com/javascript-sleep/const isCorrect = await sampleFetch(confirmationCode);

    if (isCorrect) {
      setIsPasswordCorrect(true);
    } else {
      setIsPasswordCorrect(false);
      setHasError(true);
    }

    // Confirmamos el código, podemos quitar el mensaje// de cargando:
    setIsLoading(false);
  };

  return (
    <>
      {/* ... */}

      {isLoading && <p>Cargando...</p>}
      {hasError && <p>Error...</p>}

      <input
        value={confirmationCode}
        onChange{setConfirmationCodeOnChange}
      />

      <button onClick={confirmPasswordOnClick}>
        Comprobar
      </button>


      {/* ... */}
    </>
  );
}

Bueno, tampoco es taaaan complicado. Solo debemos modificar los valores de 3 estados diferentes dependiendo de 3 posibles situaciones diferentes. ¡Pero no te emociones! Aún falta una última confirmación antes de poder realizar la eliminación.

Más estados…

Cuando isPasswordCorrect sea true debemos esconder el formulario. Y en su lugar, le mostraremos al usuario los botones de continuar o cancelar.

<button
  onClick={/* isActive debe volverse true */}
>
  Sí, eliminar
</button>
<button onClick={
  {/*
    debemos volver al estado inicial, el usuario
    debe volver a escribir la contraseña y no hay
    mensajes de error o cargando
  */}
}>
  No, volver
</button>

Enfoquémonos en el botón de cancelar.

El usuario se está arrepintiendo. Esto en código significa que debemos retroceder un paso atrás y cambiar el valor de isPasswordCorrect a false para retroceder hasta la pantalla de escribir el código de seguridad.

¡PEEEERO!

Ir SOLO un paso atrás sería un error, ya que nuestro input seguiría teniendo el código correcto. ¡No lo hemos borrado! No hemos reseteado el valor de confirmationCode. Así que eso es exactamente lo que vamos a hacer: una función para devolver todos nuestros estados a su valor inicial.

functionComponent(props) {
  // ...const resetAllStates = () => {
    setConfirmationCode('');
    setIsLoading(false);
    setHasError(false);
    setIsPasswordCorrect(false);
  };

  return (
    {/* ... */}

    {isPasswordCorrect && (
      <><p>¿Seguro que quieres eliminar?</p><buttononClick={/* … */}>
          Sí, eliminar
        </button><buttononClick={resetAllStates}>
          No, volver
        </button></>
    )}

    {/* ... */}
  );
}

Aún más estados…

Ahora necesitamos un nuevo estado para verificar si la eliminación ha sido realizada, lo llamaremos isActive. Al crearlo tendremos todo listo para confirmar la eliminación: la función setIsActive.

functionComponent(props) {
  const [confirmationCode, setConfirmationCode] = useState('');
  const [isLoading, setIsLoading] = useState(false);
  const [hasError, setHasError] = useState(false);
  const [isPasswordCorrect, setIsPasswordCorrect] = useState(false);
  const [isActive, setIsActive] = useState(true);

  const setConfirmationCodeOnChange = (e) => {/* … */};
  const confirmPasswordOnClick = (e) => {/* … */};
  const resetAllStates = () => {/* … */};

  return (
    {/* ... */}

    {isPasswordCorrect && (
      <><p>¿Seguro que quieres eliminar?</p><buttononClick={() => setIsActive(false)}>
          Sí, eliminar
        </button><buttononClick={resetAllStates}>
          No, volver
        </button></>
    )}

    {/* ... */}
  );
}

¡Perfecto! Tenemos todo listo para construir la última sección de nuestro componente: la pantalla de la eliminación completada. No hay nuevos estados que crear. Solo debemos convertir isActive en true.

functionComponent(props) {
  // ...return (
    {/* ... */}

    {!isActive && (
      <div><h2>Eliminación exitosa</h2><buttononClick={() => setIsActive(true)>
          Recuperar, cancelar la eliminación
        </button></div>
    )}

    {/* ... */}
  );
}

¡Listo! Pero, espera. Cuando usamos el botón de recuperar solo volvemos a la segunda pantalla, la que nos pregunta si estamos seguros de eliminar.

¿Por que pasa esto?

Así como antes, todos los estados siguen con sus últimos valores. Debemos avisarles que, de nuevo, deben volver a sus valores iniciales. Hay dos formas de hacerlo:

La primera (y un poco más compleja) es usar el Hook useEffect para “escuchar” los cambios de isActive y llamar a la función de resetAllStates cuando isActive se esté convirtiendo en true.

useEffect(() => {
    if (isActive) {
      resetAllStates();
    }
  }, [isActive]);

Esta es la mejor solución si debemos comunicarnos con alguna API para revertir los últimos cambios, porque podrás hacer el llamado a la API justo antes de resetear todos los estados. Incluso podrías mostrar un mensaje de loading mientras obtienes respuestas.

Pero hay otra forma mucho más sencilla y que podemos usar cuando NO necesitemos hacer llamados al backend para volver al estado anterior. ¿Recuerdas la función resetAllStates? Bueno, podemos modificarla un poco para resetear también el estado de isActive.

const reset = () => {
    setConfirmationCode('');
    setIsLoading(false);
    setHasError(false);
    setIsPasswordCorrect(false);
    setIsActive(true);
};

¡Y listo! Ahora sí nuestro componente funciona totalmente. Solo te aviso que si los requerimientos cambian, situación para la que debemos estar preparados, tendremos que hacer cambios en muchos lugares.

Esto se debe a que estamos trabajando con estados independientes, pero que se relacionan muy fuertemente con una interfaz que cambia mucho y que a final de cuentas depende de todos estos estados.

Pero no te preocupes. Vamos a refactorizar un poco con ayuda de useReducer.

Puedes encontrar el código completo de nuestro componente en: github.com/juandc/usestate-vs-usereducer/blob/master/usestate.js.

Programación Imperativa vs. Declarativa

¡Te dije que esto iba de paradigmas!

Al programar de forma imperativa debemos escribir código que nos indica paso a paso cómo evoluciona nuestra aplicación. En cambio, al programar de forma declarativa le damos más importancia a qué vamos hacer, luego nos encargaremos de cómo lo debemos hacer.

👉 https://platzi.com/funcional

Apliquemos este concepto en el estado de nuestros componentes.

Nuestro problema es que trabajamos con muchos estados independientes, pero debemos cambiarlos a casi todos cuando algo pasa en nuestra interfaz. Y si los requerimientos cambian, vamos a necesitar nuevos estados y aumentar la complejidad de nuestro componente.

Pero podemos verlo desde otro punto de vista.

¿Qué tal si pensamos como un usuario de nuestra aplicación? ¿Qué tal si no tuviéramos que llamar a todas esas funciones para actualizar cada uno de los estados? ¿Qué tal si pensamos en eventos en vez de valores de los estados?

En otras palabras, pensemos en las acciones o eventos que los usuarios perciben de la aplicación: escribir la contraseña, confirmar si es correcta, mostrar un mensaje de error si es incorrecta, cancelar, recuperar, etc.

Esta es la forma de pensar con useReducer.

Cómo funciona useReducer:

Lo primero que haremos será crear una función reducer para definir las acciones o eventos de nuestro componente. Recibimos dos parámetros:

  • state: el estado de nuestro componente antes de realizar algún cambio.
  • action: un objeto con estos otros dos elementos.
    type el nombre del evento que cambiará nuestro estado.
    payload: es opcional, lo podemos utilizar para enviar información extra a la actualización de estado.

En esta función vamos a definir todos los eventos de nuestro componente utilizando un switch.

functionreducer(state, action) {
  switch(action.type) {
    case ‘NOMBRE_DEL_EVENTO’: {
      return {/* NUEVO ESTADO */};
    }
    case ‘NOMBRE_DE_OTRO_EVENTO’: {
      return {/* OTRO NUEVO ESTADO */};
    }
  }
}

Esta función la debemos enviar como primer argumento del Hook useReducer. Además, debemos definir un objeto con el estado inicial. Este Hook nos devolverá el estado (state) y una función dispatch para enviar eventos y actualizar el estado.

const initialState = { /* … */ };

const [state, dispatch] = useReducer(reducer, initialState);

Lógica con useReducer:

El primer paso para implementar useReducer es definir el estado inicial. Vamos a usar los mismos nombres que usamos en useState.

const initialState = {
  confirmationCode: '',
  isLoading: false,
  hasError: false,
  isPasswordCorrect: false,
  isActive: true,
};

Luego de esto vamos a crear una función confirmationReducer para definir las acciones o eventos que perciben los usuarios en la aplicación.

  • WRITE_CONFIRMATION_CODE: llamaremos a este evento cada vez que el usuario escriba el código de confirmación.
  • START_CONFIRMATION: el usuario debe ver un mensaje de “cargando”. Aún no sabemos si el código es correcto o incorrecto, así que no mostraremos ningún error.
  • CONFIRMATION_FAILED: si el código de confirmación es incorrecto debemos apagar el mensaje de cargando, asegurarnos de que isPasswordCorrect siga siendo false y mostrar un mensaje de error para que el usuario vuelva a intentar.
  • CONFIRMATION_SUCCESS: si el código de confirmación es correcto debemos cambiar isPasswordCorrect a true, ocultar todos los mensajes de “cargando” o “error” y mostrar la vista de “¿estás seguro de que quieres eliminar?”.
  • DEACTIVATE: si el usuario acepta eliminar debemos convertir isActive a false.
  • RESET: el usuario debe poder cancelar la eliminación en cualquier momento.
const confirmationReducer = (state, action) => {
  switch (action.type) {
    case'WRITE_CONFIRMATION_CODE': {
      return {
        ...state,
        confirmationCode: action.payload,
      };
    }
    case'START_CONFIRMATION': {
      return {
        ...state,
        isLoading: true,
        hasError: false,
      };
    }
    case'CONFIRMATION_FAILED': {
      return {
        ...state,
        isPasswordCorrect: false,
        isLoading: false,
        hasError: true,
      };
    }
    case'CONFIRMATION_SUCCESS': {
      return {
        ...state,
        isPasswordCorrect: true,
        isLoading: false,
        hasError: false,
      };
    }
    case'DEACTIVATE': {
      return {
        ...state,
        isActive: false,
      };
    }
    case'RESET': {
      return { ...initialState };
    }
    default: return state;
  }
}

Ahora podemos llamar a la función dispatch cada vez que el usuario o la aplicación lo necesiten.

const Component = () => {
  const [state, dispatch] = useReducer(confirmationReducer, initialState);

  const {
    confirmationCode,
    isLoading,
    hasError,
    isPasswordCorrect,
    isActive,
  } = state;

  // …return (/* … */);
};

Primer dispatch: el usuario escribe el código de seguridad.

const Component = () => {
  // ...const setConfirmationCodeOnChange = (e) => {
    dispatch({
      type: ‘WRITE_CONFIRMATION_CODE’,
      payload: e.currentTarget.value,
    });
  }
  
  return (
    <>
      {/* ... */}

      <input
        value={confirmationCode}
        onChange{setConfirmationCodeOnChange}
      />

      {/* ... */}
    </>
  );
};

Segundo dispatch: comprobar si el código de seguridad es correcto y mostrar los mensajes de error o cargando.

const Component = () => {
  // …const confirmPasswordOnClick = async () => {
    dispatch({ type: 'START_CONFIRMATION' });

    const isCorrect = await sampleFetch(confirmationCode);

    if (isCorrect) {
      dispatch({ type: 'CONFIRMATION_SUCCESS' });
    } else {
      dispatch({ type: 'CONFIRMATION_FAILED' });
    }
  };

  return (
    <>
      {/* ... */}

      {isLoading && <p>Cargando...</p>}
      {hasError && <p>Error...</p>}

      <input
        value={confirmationCode}
        onChange{setConfirmationCodeOnChange}
      />

      <button onClick={confirmPasswordOnClick}>
        Comprobar
      </button>

      {/* ... */}
    </>
  );
};

Tercer y cuarto dispatch: botones de confirmar o cancelar la eliminación.

const Component = () => {
  // …return (
    {/* ... */}

    {isPasswordCorrect && (
      <><p>¿Seguro que quieres eliminar?</p><buttononClick={() => {
            dispatch({ type: 'DEACTIVATE' });
          }}
        >
          Sí, eliminar
        </button><buttononClick={() => {
            dispatch({ type: 'RESET' });
          }}
        >
          No, volver
        </button></>
    )}

    {/* ... */}
  );
};

Quinto (y último) dispatch: botón para deshacer la eliminación y volver al estado inicial.

{!isActive && (
  <div><h2>Eliminación exitosa</h2><buttononClick={() => {
        dispatch({ type: ‘RESET’ });
      }}
    >
      Recuperar, cancelar la eliminación
    </button></div>
)}

¡Y listo! Esto fue la implementación de useReducer para manejar el estado de nuestro componente.

Puedes encontrar el código completo de nuestro componente en: github.com/juandc/usestate-vs-usereducer/blob/master/usereducer.js.

Referencias:

👉 https://youtu.be/o-nCM1857AQ
👉 https://kentcdodds.com/blog/should-i-usestate-or-usereducer
👉 https://dev.to/spukas/3-reasons-to-usereducer-over-usestate-43ad
👉 https://dev.to/macmacky/usereducer-redux-s-reducer-4jee
👉 https://www.simplethread.com/cant-replace-redux-with-hooks/
👉 https://www.robinwieruch.de/redux-javascript
👉 https://www.robinwieruch.de/redux-vs-usereducer

Conclusiones

Como te dije al principio, esta comparación no es entre useState y useReducer. Más bien, es un recordatorio de que no podemos limitar nuestra mente a un solo paradigma. Y en este caso, manejar tantos estados independientes que dependen unos de otros puede no ser la mejor opción.

Te reto a resolver los siguientes ejercicios:

  • Añade un nuevo requerimiento a nuestro componente. Por ejemplo: que después de 3 intentos debas esperar un minuto para poder volver a intentar escribir el código de seguridad.
  • Aplica todo lo que aprendimos sobre manejo del estado con programación declarativa usando useState.
  • Construye tu propio useReducer. Yo lo llamaré useReducerPlagio.

Finalmente, te invito a tomar el Curso React.js: Manejo Profesional del Estado. Vamos a descubrir la diversidad de herramientas y paradigmas que nos ofrece React para manejar el estado y cómo han evolucionado estas soluciones a lo largo del tiempo.

#NuncaParesDeAprender 🤓💚

Juan
Juan
juandc

211925Puntos

hace 4 años

Todas sus entradas
Escribe tu comentario
+ 2
Ordenar por:
7
44995Puntos

¡Increíble post Juan! 😮

10
211925Puntos
4 años

¡Gracias, Facu!

Mi mejor resumen sería: “useState nos permite crear estados independientes ilimitados. Y useReducer soluciona los problemas de exagerar la cantidad”. 😄

3
7187Puntos

muy buen aporte, y claro con ejemplos y todo me ayudo a entender unas cosas que no tenia muy claras gracias!

2
16588Puntos

Muchas gracias por la explicación. 😄

2
33485Puntos

¡Ya me quedo clarísimo, gracias Juan eres grande!

2
1118Puntos

Excelente Juan, me ayudaste men

2
11022Puntos

Excelente, me ayuda a entender mejor useReducer

2

Gracias Juan David hapasado un tiempo desde tu publicación y sigues ayudando … Thanks.

2
11290Puntos

Excelente post, justo lo que necesitaba gracias 😄

1
17789Puntos

Tengo una duda increible

Tengo un contenedor llamdo Wrapper

import React from “react”;
import { Header } from ‘…/Header’;
import { DataContainer } from “…/DataContainer”;
import ‘./Wrapper.css’

function Wrapper(){
return(
<div className=“Wrapper” id=“Wrapper”>
<Header></Header>
<DataContainer></DataContainer>
</div>
)

}

export { Wrapper };

que llama al contenedor DataContainer

import React from "react"
import { Login } from “…/Login”
// import Register from "…/Register"
import ‘./DataContainer.css’

function DataContainer(){
return(
<div className=“DataContainer” id=“DataContainer”>
<Login
name=“active”
>
</Login>
{/* <Register></Register> */}
</div>
)

}

export { DataContainer };

Que llama al login

import ‘./Login.css’;
import React, { useState, useEffect } from ‘react’;
function Login(props){
const [state, setState] = React.useState({
value:’’,
})
console.log(‘state:’+state.value)
console.log(‘state:’+state)
console.log(‘setState:’+setState)
if(state.value ===false){
setState({
…state,
value: true,
})
}
return(
<div>
{state.value && (
<h1>Hola mundo<h1>
}
</div>
)
}
export { Login };

Hasta aquí todo bien.

El problema es que en el Header que es otro archivo creo el quetiene unos links creo el onClick

import React from ‘react’;
import ‘./Header.css’;
import Fondo_noche from '…/assets/icons/Logo.svg’
import { Login } from ‘…/Login’;
function Header() {
return (
<div>
<a className=“header__container__user__container__ingresar"
id=“header__container__user__container__ingresar” href=”#"
onClick={()=>Login()}
>Ingresar</a>
</div>
)
}
export { Header }

Cuando doy clic en el link sale esto y ya he leido y no doy con el problema

Uncaught Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:

  1. You might have mismatching versions of React and the renderer (such as React DOM)
  2. You might be breaking the Rules of Hooks
  3. You might have more than one copy of React in the same app
    See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.
    at Object.throwInvalidHookError (react-dom.development.js:16227:1)
    at Object.useState (react.development.js:1622:1)
    at Login (index.js:7:1)
    at onClick (index.js:22:1)
    at HTMLUnknownElement.callCallback (react-dom.development.js:4164:1)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:4213:1)
    at invokeGuardedCallback (react-dom.development.js:4277:1)
    at invokeGuardedCallbackAndCatchFirstError (react-dom.development.js:4291:1)
    at executeDispatch (react-dom.development.js:9041:1)
    at processDispatchQueueItemsInOrder (react-dom.development.js:9073:1)
    throwInvalidHookError @ react-dom.development.js:16227
    useState @ react.development.js:1622
    Login @ index.js:7
    onClick @ index.js:22
    callCallback @ react-dom.development.js:4164
    invokeGuardedCallbackDev @ react-dom.development.js:4213
    invokeGuardedCallback @ react-dom.development.js:4277
    invokeGuardedCallbackAndCatchFirstError @ react-dom.development.js:4291
    executeDispatch @ react-dom.development.js:9041
    processDispatchQueueItemsInOrder @ react-dom.development.js:9073
    processDispatchQueue @ react-dom.development.js:9086
    dispatchEventsForPlugins @ react-dom.development.js:9097
    (anónimas) @ react-dom.development.js:9288
    batchedUpdates$1 @ react-dom.development.js:26140
    batchedUpdates @ react-dom.development.js:3991
    dispatchEventForPluginEventSystem @ react-dom.development.js:9287
    dispatchEventWithEnableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay @ react-dom.development.js:6465
    dispatchEvent @ react-dom.development.js:6457
    dispatchDiscreteEvent @ react-dom.development.js:6430