17

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

77529Puntos

hace 2 meses

Curso de React Avanzado
Curso de React Avanzado

Curso de React Avanzado

Crea aplicaciones móviles en ReactJS. Genera consultas en GraphQL y gestiona usuarios. Implementa Testing básico con Cypress y convierte tus apps en PWA con herramientas como Hooks, React Apollo, Reach Router y JSON Web Tokens.

Vamos a estudiar cómo aplicar el principio de la Programación Declarativa al manejo del Estado de nuestros componentes 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</<span class="hljs-name">h2>

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

    {isPasswordCorrect && (
       {/* … */}
    )}
  </<span class="hljs-name">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 && (
  <>
    

Por favor, escribe el código de seguridad para comprobar que quieres eliminar.

{isLoading &&

Cargando...

} {hasError &&

Error...

} <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={<span class="hljs-comment">/*
  - 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?</<span class="hljs-name">p><buttononClick={/* isActivedebevolversetrue */}>
      Sí, eliminar
    </<span class="hljs-name">button>
    <buttononClick={/*
      debemosvolveralestadoinicial, elusuariodebevolveraescribirlacontraseñaynohaymensajesdeerrorocargando
    */}>
      No, volver
    </<span class="hljs-name">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</<span class="hljs-name">h2><buttononClick={/* isActivedebevolversetrue */}>
       Recuperar, cancelar la eliminación
    </<span class="hljs-name">button>
  </<span class="hljs-name">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 && 

Cargando...

} {hasError &&

Error...

} <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 &&

Cargando...

} {hasError &&

Error...

} <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 && 

Cargando...

} {hasError &&

Error...

} <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?</<span class="hljs-name">p><buttononClick={/* … */}>
          Sí, eliminar
        </<span class="hljs-name">button>
        <buttononClick={resetAllStates}>
          No, volver
        </<span class="hljs-name">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?</<span class="hljs-name">p><buttononClick={() => setIsActive(false)}>
          Sí, eliminar
        </<span class="hljs-name">button>
        <buttononClick={resetAllStates}>
          No, volver
        </<span class="hljs-name">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</<span class="hljs-name">h2><buttononClick={() => setIsActive(true)>
          Recuperar, cancelar la eliminación
        </<span class="hljs-name">button>
      </<span class="hljs-name">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 && 

Cargando...

} {hasError &&

Error...

} <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?</<span class="hljs-name">p><buttononClick={() => {
            dispatch({ type: 'DEACTIVATE' });
          }}
        >
          Sí, eliminar
        </<span class="hljs-name">button>
        <buttononClick={() => {
            dispatch({ type: 'RESET' });
          }}
        >
          No, volver
        </<span class="hljs-name">button>
      
    )}

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

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

{!isActive && (
  <div><h2>Eliminación exitosa</<span class="hljs-name">h2><buttononClick={() => {
        dispatch({ type: ‘RESET’ });
      }}
    >
      Recuperar, cancelar la eliminación
    </<span class="hljs-name">button>
  </<span class="hljs-name">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 de React Avanzado. Vamos a estudiar las nuevas características de React (como Hooks y Context) y sus mejores prácticas para construir nuestro proyecto: un clon de Instagram para mascotas.

#NuncaParesDeAprender 🤓💚

Curso de React Avanzado
Curso de React Avanzado

Curso de React Avanzado

Crea aplicaciones móviles en ReactJS. Genera consultas en GraphQL y gestiona usuarios. Implementa Testing básico con Cypress y convierte tus apps en PWA con herramientas como Hooks, React Apollo, Reach Router y JSON Web Tokens.
Juan David
Juan David
@juandc

77529Puntos

hace 2 meses

Todas sus entradas
Escribe tu comentario
+ 2
3
24194Puntos

¡Increíble post Juan! 😮

1
77529Puntos
2 meses

¡Gracias, Facu!

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

3
5581Puntos

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