100% encontró este documento útil (1 voto)
699 vistas

React Hooks

ESTE DOC ES MUY IMPORT

Cargado por

Andres Lopez
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
100% encontró este documento útil (1 voto)
699 vistas

React Hooks

ESTE DOC ES MUY IMPORT

Cargado por

Andres Lopez
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
Está en la página 1/ 55

React Hooks Manual Desde Cero

Juan Correa
Este libro está a la venta en http://leanpub.com/react-hooks

Esta versión se publicó en 2020-09-29

Éste es un libro de Leanpub. Leanpub anima a los autores y publicadoras con el proceso de
publicación. Lean Publishing es el acto de publicar un libro en progreso usando herramientas
sencillas y muchas iteraciones para obtener retroalimentación del lector hasta conseguir el libro
adecuado.

© 2020 Juan Correa


ÍNDICE GENERAL

Índice general

Acerca de este Ebook . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1

Introduccion a los React Hooks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2

Hook de estado: useState . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4


Comparacion de componente tipo clase y funcional al usar variables de estado . . . . . . . 4
Actualizando Arrays con useState . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
Actualizando Objetos con useState . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
Recapituacion del hook useState . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10

Hook de efectos secundarios useEffect . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12


Caracteristicas de useEffect . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
Peticion de datos (componentDidMount y componentDidUpdate) . . . . . . . . . . . . . . . 13
Iniciar y eliminar subscripciones (componentDidMount, componentWillUnmount) . . . . 16
Recapitulacion del hook useEffect . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17

Hook para memorizar funciones useCallback . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18


Ejemplo de optimización con useCallback (incluyendo React.memo) . . . . . . . . . . . . . 19
¿Debo memorizar funciones todo el tiempo? . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
Recapitulacion del hook useCallback . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24

Hook para optimizar cálculos useMemo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25


PureComponent, React.memo y useMemo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
Recapitulacion del hook useMemo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29

Hook para estados complejos useReducer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30


Funciones reductoras (reducers) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
Ejemplo de un componente con useReducer . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
Diferencia entre useReducer y Redux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
Recapitulacion del hook useReducer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41

Hook para manejar la API de Context useContext . . . . . . . . . . . . . . . . . . . . . . . . . . 42


Ejemplo de Context en componente clase y useContext en componente funcional . . . . . 42
Recapitulacion del hook useContext . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47

Hook para trabajar con referencias useRef . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48

Ebook gratuito por Developero


ÍNDICE GENERAL

Emular variables de instancia en componente funcional (this) . . . . . . . . . . . . . . . . . 49


Recapitulacion del hook useRef . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50

Siguientes pasos en tu carrera de React Developer . . . . . . . . . . . . . . . . . . . . . . . . . 51

Ebook gratuito por Developero


Acerca de este Ebook
Este es un Ebook creado por Juan Correa, fundador de Developero, espacio para compartir
conocimientos sobre desarrollo de software.
¿Tienes la versión más reciente de este ebook?
Obtén todas las actualizaciones descargando este ebook en los siguientes enlaces:
Leanpub¹ Ojo, lo puedes obtener de manera gratuita seleccionado el precio igual a cero pero te
agradezco si decides donar voluntariamente :)
Comparte el conocimiento.
¹https://leanpub.com/react-hooks
Introduccion a los React Hooks
Los React Hooks fueron incorporados a partir de la versión 16.8 de React y nos permiten utilizar las
características de esta biblioteca en componentes funcionales.
Para dar solo un ejemplo: a partir de ahora puedes declarar variables de estado en componentes
funcionales, algo que antes era posible sólo en componentes de tipo clase.
¿Qué aspecto tiene el uso de los hooks?
Un ejemplo a continuación.
Ejemplo de useState

1 import React, { useState } from "react";


2
3 const Count = () => {
4 const [count, setCount] = React.useState(0);
5
6 const handleClick = () => {
7 setCount(count + 1);
8 };
9
10 return (
11 <>
12 <p>Clicks: {count}</p>
13 <button onClick={handleClick}>Clickeame</button>
14 </>
15 );
16 };

Esto es sólo una muestra introductoria para que veas como luce un hook. No te preocupes si este
ejemplo no hace sentido. Ya veremos en capítulos posteriores cada hook paso a paso :).
Conforme avances en estas páginas te darás cuenta del PODER de los hooks y los nuevos caminos
que se abren para ti al momento en que diseñas tus componentes.
¿Los React Hooks van a reemplazar los componentes de tipo clase?
No realmente. Un buen acierto de React es que los componentes de tipo clase van a seguir siendo
soportados, por lo que puedes tener la tranquilidad de que tu código fuente que las contenga va a
seguir funcionando.
Introduccion a los React Hooks 3

Lo cierto es que a partir de ahora puedes elegir crear componentes de tipo clase o tipo función según
las necesidades de la aplicación y de tus criterios como desarrollador.
Al momento de escribir este manual, hay un caso de uso en el que los componentes de tipo clase
pueden hacer algo que los hooks no pueden: el manejo de límite de errores (Error Boundaries).
Si no estás familiarizado con esto, es simplemente la capacidad de un componente de manejar un
error de ejecución y te provee acceso a actuar en consecuencia, como mostrando un mensaje y
canalizar el error en un servicio de informes como Sentry. Todo esto sin tirar la aplicación.
Los hooks nos proveen de una manera más limpia para manejar la misma lógica que nos permiten
manejar los ciclos de vida de una clase como veremos en los ejemplos de cada hook.
Ya que te beneficia tener código simple en vez de complejo, probablemente termines eligiendo los
hooks la mayoría de las veces.
Aunque ojo: aún con hooks puedes terminar con código complejo si sigues malas prácticas. Este
manual te va a guiar en las buenas prácticas para que saques el máximo provecho a los hooks.
¿Qué necesidades cubren los React Hooks?
Los hooks vienen para resolver los siguientes problemas:
Reusar lógica entre componentes: La manera de reusar lógica antes de los hooks era por medio de
patrones o técnicas como los render props y los componentes de orden superior (HOC por sus siglas
en inglés).
El problema de esto es que la jerarquía de componentes tiende a terminar siendo un árbol complejo
de envolvedores (wrapper hell).
Veremos en un apartado posterior cómo puedes crear tus propios hooks, de modo que ahora podrás
reusar lógica entre componentes de una manera más limpia y sin necesidad de modificar la jerarquía
de los mismos.
Componentes complejos: En este contexto, un componente complejo es aquel que para funcionar
termina ejecutando varias sentencias de código que pueden estar o no relacionadas en los mismos
métodos de ciclos de vida de un componente.
Por ejemplo: puede que cuando se monte el componente, en el método componentDidMount,
consumas una api y en ese mismo método inicialices una o varias subscripciones de eventos del
navegador (o de algo diferente a la api).
Lo anterior ocasiona que tengamos lógica diferente mezclada en el mismo lugar.
El inconveniente de esto es que el código se hace difícil de leer y por lo tanto da pie a los errores
humanos.
Ahora pasemos a la acción y veamos cada uno de los hooks, cómo, por qué y en qué contextos
usarlos.
Te veo en el siguiente capítulo :)

Ebook gratuito por Developero


Hook de estado: useState
El hook useState te permite poder usar variables de estado dentro de un componente funcional.
Este es el hook que probablemente mas usaras por lo que comprenderlo bien va a ser muy importante.

Comparacion de componente tipo clase y funcional al


usar variables de estado
A continuacion vamos a hacer un componente de tipo clase que sirva para contar los clicks que hace
el usuario a un boton.
Uso de estado en componente tipo clase

1 class Count extends React.Component {


2 state = {
3 count: 0
4 };
5
6 handleClick = () => this.setState({ count: this.state.count + 1 });
7
8 render() {
9 const { count } = this.state;
10 return (
11 <>
12 <p>Clicks: {count}</p>
13 <button onClick={this.handleClick}>Clickeame</button>
14 </>
15 );
16 }
17 }

No usamos el constructor para iniciar el valor del state.


Lo definimos por fuera del constructor basados en la sintaxis alternativa, es decir, declarar el estado
como una propiedad de la clase desde fuera del constructor.
Ahora vamos a hacer el ejemplo equivalente en componente funcional utilizando el hook useState.
Hook de estado: useState 5

Uso de estado en componente funcional

1 const Count = () => {


2 const [count, setCount] = useState(0);
3
4 const handleClick = () => {
5 setCount(count + 1);
6 };
7
8 return (
9 <>
10 <p>Clicks: {count}</p>
11 <button onClick={handleClick}>Clickeame</button>
12 </>
13 );
14 };

Mucho mejor! el componente es mas simple, mas limpio y con el mismo poder del manejo de estado
de React :D.
Codigo fuente para ejecutar y editar aqui².
El hook useState nos retorna siempre un array con dos elementos.
Destructuracion de array en el hook useState

1 const [count, setCount] = useState(0);

La sintaxis que vemos con los corchetes no es de React sino de ES6 y es llamada destructuracion.
En la posicion cero del array nos retorna la variable de estado y la posicion uno nos retorna una
funcion que nos sirve para actualizar la variable de estado.
Por lo tanto, lo siguiente es igualmente valido para Javascript.
declaracion de variables de estado sin destructurar

1 const MyComponent = () => {


2 const state = React.useState(null);
3 const myState = state[0];
4 const setMyState = state[1];
5 };

Como puedes ver, es mas limpio usar la destructuracion por lo que siempre veras ejemplos de ese
modo.
²https://codesandbox.io/s/use-state-5u0tf?file=/src/App.js

Ebook gratuito por Developero


Hook de estado: useState 6

Y al ser destruturado, podemos nombrar las dos variables de la manera que convenga mejor a
nuestros intereses.
Lo recomendado es que sean nombres con significado, que indiquen cual es su proposito.
Ya que la segunda variable es una funcion que actualiza el estado, en todos los ejemplos vas a
encontrar que inicia con “set” y termina con el nombre de la variable de estado.
El hook useState recibe por parametro el valor inicial de la variable de estado
A diferencia del estado en un componente de tipo clase, la variable de estado usando este hook no
tiene que ser necesariamente un objeto.
Nuestro estado puede ser una variable de los siguientes tipos:

• String.
• Boolean.
• Number.
• Float.
• Null.
• Undefined.
• Object.
• Array.

Ejemplo de tipo de variable inicial en useState

1 const MyComponent = () => {


2 const [stateVariable, setStateVariable] = React.useState(false); // <<< pasamos un\
3 booleano
4 };

Para valores de estado iniciales que sean resultado de un calculo costoso, puedes pasar como valor
inicial una funcion que retorne dicho valor calculado.
Ejemplo de valor inicial usando una funcion

1 const MyComponent = () => {


2 const [
3 stateVariable,
4 setStateVariable
5 ] = React.useState(() => someExpensiveCalc(); // aqui
6 };

Ten en cuenta que los valores iniciales del estado se ejecutan solo una vez y es cuando se monta el
componente

Ebook gratuito por Developero


Hook de estado: useState 7

Puedes declarar mas de un estado local con useState


Va a ser comun que necesites mas de una variable de estado en un componente.
En lugar de tener una sola variable de estado como un objeto que contenga multiples valores,
probablemente sea mejor separar por “preocupaciones” (separation of concerns principle).
Ejemplo multiples variables de estado

1 // dentro de un componente funcional


2
3 const [username, setUsername] = useState('');
4 const [isAdult, setIsAdult] = useState(false);
5 const [age, setAge] = useState(0);
6 const [userTags, setUserTags] = useState([]);

Te recomiendo que trates de mantener tus variables de estado lo mas simples posibles y separarlas
en diferentes variables cada que puedas.
Sin embargo, a veces vas a necesitar usar arrays u objetos como variables de estado, en esos casos
te recomiendo que trates de mantenerlos lo mas planos posibles.
Para estructuras mas complejas (un objeto de objetos, un array de objetos que tienen objetos, etc);
lo recomendable es usar el hook useRedux como veremos en su capitulo correspondiente.

Actualizando Arrays con useState


Es importante que tengas en cuenta la manera correcta de actualizar el estado cuando usas este tipo
de datos ya que puedes caer en comportamientos inesperados.
Para actualizar el estado de manera adecuada cuando manejas arrays, necesitas pasar el array
completo en la funcion que actualiza el estado.
A continuacion unos ejemplos de como NO y como SI agregar un nuevo elemento.
Ejemplo de agregar un nuevo elemento a un array como variable de estado

1 const Example = () => {


2 const [tags, setTags] = useState([]);
3
4 // NO
5 tags.push('new value');
6
7 // SI
8 setTags([...tags, 'new value']);
9 }

Ebook gratuito por Developero


Hook de estado: useState 8

La razon de por que no debemos mutar nuestro array con metodos como push, pop, etc, es porque
React no detecta esos cambios y no hara un re renderizado.
Esta sintaxis setTags([...tags, 'new value']); es propia de ES6 y se llama spread operator.
Lo que hacemos con ella es crear un NUEVO array cuyo contenido va a ser igual a todo lo que tenga
el array tags y aparte, un nuevo valor que es 'new value'.
Lo mismo aplica si tienes un array de objetos en vez de un array de cadenas: setTags([...tags, {
value:'new value'}]) (asumiendo que tags es un array de objetos).

Modificar el contenido de un Array como variable de estado


Para actualizar elementos existentes dentro de un array podemos usar diferentes estrategias.
La mas facil es crear un nuevo array con el valor modificado usando .map como se ve a continuacion.
Ejemplo de modificar un elemento existente en un array como variable de estado

1 const Example = () => {


2 const [tags, setTags] = useState(["one", "two", "three"]);
3
4 // update
5 const indexToUpdate = 1;
6 const newValue = "four?";
7 const tagsUpdated = tags.map((value, index) => {
8 if (index === indexToUpdate) {
9 return newValue;
10 }
11
12 return value;
13 });
14
15 setTags(tagsUpdated);
16 };

Ahora bien, un array puede contener objetos en vez de valores primitivos como cadenas. Veamos el
ejemplo de como actualizar cuando es un array de objetos.

Ebook gratuito por Developero


Hook de estado: useState 9

Ejemplo de modificar un elemento de un array de objetos como variable de estado

1 const Example = () => {


2 const [tags, setTags] = useState([
3 { id: 1, value: "one" },
4 { id: 2, value: "two" },
5 { id: 3, value: "three" }
6 ]);
7
8 // update
9 const idToUpdate = 2;
10 const newValue = { id: 2, value: "four?" };
11 const tagsUpdated = tags.map((value) => {
12 if (value.id === idToUpdate) {
13 return newValue;
14 }
15
16 return value;
17 });
18
19 setTags(tagsUpdated);
20 };

Eliminar un elemento de un array como variable de estado


Para eliminar un elemento necesitamos pasar un nuevo array sin el elemento que queremos eliminar.
La manera que es mas comoda para hacerlo es usando la funcion .filter que viene en el prototype
de Array.
Ejemplo de eliminar un elemento de un array como variable de estado

1 const Example = () => {


2 const [tags, setTags] = useState([
3 { id: 1, value: "one" },
4 { id: 2, value: "two" },
5 { id: 3, value: "three" }
6 ]);
7
8 // remove
9 const idToUpdate = 2;
10
11 const tagsUpdated = tags.filter(({ id }) => id !== idToUpdate);
12

Ebook gratuito por Developero


Hook de estado: useState 10

13 setTags(tagsUpdated);
14 };

Actualizando Objetos con useState


Al igual que cuando actualizamos un array, cuando actualizamos un objeto tambien necesitamos
pasar el objeto completo en la funcion que actualiza dicho estado.
Ejemplo de agregar valores a un objeto como variable de estado
1 const Example = () => {
2 const [userData, setUserData] = useState({
3 name: "",
4 age: ""
5 });
6
7 // NO
8 userData.name = "Juanito";
9
10 // SI
11 setUserData({ ...userData, name: "Juanito Banana" });
12 setUserData({ ...userData, age: "20" });
13
14 // SI
15 const property = 'name';
16
17 setUserData({
18 ...userData,
19 [property]: "Juanito Banana"
20 });
21 };

Nota que tambien estamos usando el spread operator para crear un nuevo objeto con las
propiedades correspondientes.
En el segundo ejemplo estamos modificando la propiedad name de manera dinamica usando
[property]: "Juanito Banana" donde property = 'name';.
Para editar una propiedad es exactamente igual a como cuando le asignamos un valor.

Recapituacion del hook useState


En este capitulo hemos visto los principales casos de uso que vas a necesitar en tu dia a dia
programando:

Ebook gratuito por Developero


Hook de estado: useState 11

• Definir uno o varios hooks.


• Definir el estado inicial del hook considerando valores iniciales complejos.
• Actualizar un array como variable de estado.
• Actualiar un objeto como variable de estado.

¡Te veo en el siguiente capitulo!

Ebook gratuito por Developero


Hook de efectos secundarios
useEffect
EL hook useEffect es el segundo hook que más vas a usar debido a que nos ayuda a todo lo relativo
de efectos secundarios.
¿Y qué rayos es un efecto secundario, Juanito?
Un efecto secundario o simplemente efecto, lo puedes ver como la ley de causa y efecto: todo efecto
es como consecuencia de una o varias causas que lo provocan.
Tal como lo dice la documentacion de React, los siguientes son ejemplos de efectos secundarios:

• Peticiones de datos.
• Establecimiento de suscripciones.
• Actualizaciones manuales del DOM.

Esto ya se viene haciendo en React en los componentes de tipo clase por medio de los ciclos de vida.
Pues bien, estos efectos secundarios ahora también los podemos hacer en componentes funcionales.
La manera en que luce el hook useEffect es la siguiente.
Ejemplo del hook useEffect
1 const MyComponent = () => {
2 useEffect(() => {
3 console.log('useEffect ejecutado');
4 })
5
6 return ':)';
7 }

Vamos a entrar más en materia a continuación.

Caracteristicas de useEffect
1. Recibe dos parámetros: el primero una función y el segundo un array cuyos valores serán
variables de las que depende nuestro efecto (este array es opcional).
2. Se ejecuta en cada renderizado incluyendo el primero.
3. Puedes usar más de un useEffect dentro de un componente.
4. Está diseñado para que si la función que pasamos por parámetro retorna a su vez otra función,
React va a ejecutar dicha función cuando se desmonte el componente.
Hook de efectos secundarios useEffect 13

Ejemplos de useEffect

1 const MyComponent = ({ someProp }) => {


2 useEffect(() => {
3 console.log("se ejecuta cada render");
4 console.log("incluso el inicial");
5 });
6
7 useEffect(() => {
8 console.log("nuevo valor de someProp", someProp);
9 }, [someProp]);
10
11 useEffect(() => {
12 // code
13 return () => console.log("componente desmontado");
14 });
15
16 return ":)";
17 };

Esto es lo mínimo necesario que necesitas saber para comenzar a usar useEffect.
Pero para ponerla más fácil, vamos a ver ejemplos de casos prácticos del mundo real en el uso de
este hook.

Peticion de datos (componentDidMount y


componentDidUpdate)
El siguiente ejemplo va a ser sólo para fines educativos y es simular una llamada a una API.
Lo importante de este ejemplo es comparar la llamada a una api cuando se monta un componente
cuando usas un componente de tipo clase y cómo es su equivalente con un hook.
Ejemplo de llamada a una api al montarse el componente de tipo clase

1 class PostData extends Component {


2 state = {
3 data: []
4 };
5
6 componentDidMount() {
7 const data = myApi.fakeFetch();
8 this.setState({ data });

Ebook gratuito por Developero


Hook de efectos secundarios useEffect 14

9 }
10
11 render() {
12 return this.state.data.map(
13 ({ label }) => (<p>{label}</p>)
14 );
15 }
16 }

Ahora vamos a hacer su equivalente con useEffect.


Ejemplo de llamada a una api con useEffect

1 const SecondPostData = () => {


2 const [data, setData] = useState([]);
3
4 useEffect(() => {
5 const data = myApi.fakeFetch();
6 setData(data);
7 }, []);
8
9 return data.map(({ label }) =>
10 <p>{label}</p>
11 );
12 };

Como puedes ver, el código es menos y más simple.


Este efecto se va a ejecutar sólo cuando el componente se monte la primera vez y no volverá a
ejecutarse.
Para lograr esto pasamos a useEffect un array vacío como segundo parámetro.
Recuerda que ese array de segundo parámetro sirve para indicarle a React las dependencias de ese
efecto.
Si el array de dependencias está vacío, no habrá nada que lo vuelva a ejecutar, por lo que hacerlo de
este modo equivale al ciclo de vida componentDidMount.
Ahora vamos a extender el ejemplo en el escenario en el que se usa el ciclo componentDidUpdate
para hacer una segunda llamada a una api cuando depende de un dato como el id de un usuario.

Ebook gratuito por Developero


Hook de efectos secundarios useEffect 15

Ejemplo de llamada a api al montarse y actualizarse un componente de tipo clase

1 class PostData extends Component {


2 state = {
3 data: []
4 };
5
6 componentDidMount() {
7 const { userId } = this.props;
8 const data = myApi.fakeFetch(userId);
9 this.setState({
10 data
11 });
12 }
13
14 // nuevo
15 componentDidUpdate(prevProps) {
16 const { userId } = this.props;
17
18 if (prevProps.userId !== userId) {
19 const data = myApi.fakeFetch(userId);
20 this.setState({
21 data
22 });
23 }
24 }
25
26 render() {
27 return this.state.data.map(
28 ({ label }) => <p>{label}</p>);
29 }
30 }

Nada fuera de lo común. Estamos haciendo una llamada a la api cada vez que el user id cambie.
Ahora veamos su equivalente en un componente funcional.

Ebook gratuito por Developero


Hook de efectos secundarios useEffect 16

Ejemplo de llamada a api al montarse y actualizarse un componente funcional

1 const SecondPostData = ({ userId }) => {


2 const [data, setData] = useState([]);
3
4 useEffect(() => {
5 const data = myApi.fakeFetch(userId);
6 setData(data);
7 }, [userId]); // nuevo
8
9 return data.map(({ label }) => <p>{label}</p>);
10 };

Como puedes ver, sólo hemos pasado la variable userId como dependencia dentro del array del
useEffect.

Esto significa que nuestro efecto se va a ejecutar al montarse el componente y cada vez que la
variable userId cambie, logrando así nuestro objetivo.
El código anterior lo puedes ejecutar y editar en este link.³

Iniciar y eliminar subscripciones


(componentDidMount, componentWillUnmount)
Vamos a usar de ejemplo la suscripción del evento de cuando se presiona la tecla Enter del teclado.
Ya que es un efecto secundario, iniciamos creando la suscripción dentro de un useEffect.
Cuando realizamos una suscripción por lo común vamos a necesitar “sanarla” o removerla cuando
se desmonta el componente como en este ejemplo.
Para ello en nuestro useEffect donde inicializamos la suscripción vamos a retornar una función que
contenga el código necesario para removerla.

³https://codesandbox.io/s/use-effect-go50j?file=/src/App.js:649-881

Ebook gratuito por Developero


Hook de efectos secundarios useEffect 17

Ejemplo de suscripción

1 const MyComponent = ({ someProp }) => {


2 useEffect(() => {
3 window.addEventListener("keydown", handleKeydown);
4
5 return () =>
6 window.removeEventListener(
7 "keydown",
8 handleKeydown,
9 );
10 });
11
12 const handleKeydown = ({ keyCode }) => {
13 const enterKeyCode = 13;
14
15 if (keyCode === enterKeyCode) {
16 alert("tecla Enter presionada");
17 }
18 };
19
20 return "Presiona Enter :)";
21 };

El mismo principio aplica para cualquier tipo de suscripciones que realices.

Recapitulacion del hook useEffect


En este capítulo hemos visto lo siguiente:

• El hook useEffect recibe dos parámetros: una función que React ejecutará en cada renderizado
y un array de dependencias como opcional.
• Para emular componentDidMount pasa un array vacío.
• Para emular componentWillUnmount retorna una función con la lógica que quieres correr
cuando se desmonte el componente, usado comúnmente en las suscripciones.
• Para emular componentDidUpdate pasa las variables dependientes del estado en el array de
dependencias.
• Puedes usar más de un useEffect en el mismo componente.

Ahora pasemos al siguiente capítulo :D

Ebook gratuito por Developero


Hook para memorizar funciones
useCallback
El hook useCallback sirve para mejorar el rendimiento de nuestros componentes en base a
memorizar funciones que se usan como callbacks.
Así es como luce:
Ejemplo de useCallback de la documentación oficial de reactjs.org

1 const memoizedCallback = useCallback(


2 () => {
3 doSomething(a, b);
4 },
5 [a, b],
6 );

Este ejemplo es sólo para que te familiarices con la manera en que se define este hook.
Antes de continuar es importante tener en cuenta ciertos detalles.
Cuando un prop cambia, se ejecuta un re render. ¿Cierto?
Pues bien, este hook es útil en el escenario en el que pasas funciones por props a componentes
hijos.
Memorizar una función ayuda a evitar re renders innecesarios debido a la manera en que funciona
Javascript como lenguaje.
Igualdad referencial en Javascript

1 true === true // true


2 false === false // true
3 1 === 1 // true
4 'a' === 'a' // true
5 {} === {} // false
6 [] === [] // false
7 () => {} === () => {} // false
8 const z = {}
9 z === z // true
Hook para memorizar funciones useCallback 19

Nota: las funciones para actualizar el estado que retorna useState tienen garantía de que serán
estables todo el tiempo, por lo que nos podemos despreocupar de ellas.
Al usar un callback memorizado logramos que no se vuelva a definir ese callback en cada render a
menos que alguna de sus dependencias cambie.
Si te suena confuso no te preocupes que para eso está este ebook :)
Vamos a ver un ejemplo a continuación, sigue leyendo :D

Ejemplo de optimización con useCallback (incluyendo


React.memo)
Antes mencioné que este hook es útil cuando pasas funciones como props a componentes hijos.
Para ilustrar bien este ejemplo, vamos a hacer una mini app que consiste en tener una lista de comida
con la capacidad de remover elementos de dicha lista.
Primero veremos el problema de los re renders innecesarios y después lo resolveremos con
React.memo y el hook useCallback.

Nota: React.memo no es el hook useMemo que veremos en su capítulo correspondiente. Si no lo


conoces, da click aquí⁴ para ver su documentación.
Vamos a hacer los siguientes componentes:

• FoodContainer: estado de lista y un input text para escribir


• FoodList: renderiza el listado de comida.
• FoodItem: renderiza un elemento de la lista de comida.

lista de alimentos

1 const food = [
2 { id: 1, name: "pizza" },
3 { id: 2, name: "hamburger" },
4 { id: 3, name: "hot-dog" },
5 { id: 4, name: "tacos" },
6 { id: 5, name: "pizza again :)" }
7 ];

FoodList container será un componente algo grande.

⁴https://es.reactjs.org/docs/react-api.html#reactmemo

Ebook gratuito por Developero


Hook para memorizar funciones useCallback 20

FoodContainer

1 const FoodContainer = () => {


2 console.log("FoodContainer rendered");
3
4 const [foodList, setFoodList] = useState(food);
5 const [textInput, setTextInput] = useState("");
6
7 const handleChange = ({ target }) =>
8 setTextInput(target.value);
9
10 const removeItem = (id) =>
11 setFoodList(foodList.filter((foodItem) =>
12 foodItem.id !== id));
13
14 return (
15 <>
16 <h2>My Food List</h2>
17 <p>
18 New food
19 <input
20 value={textInput}
21 onChange={handleChange}
22 />
23 </p>
24 <FoodList
25 foodList={foodList}
26 removeItem={removeItem}
27 />
28 </>
29 );
30 };

Ahora FoodList y FoodItem.

Ebook gratuito por Developero


Hook para memorizar funciones useCallback 21

FoodList
1 const FoodList = ({ foodList, removeItem }) => {
2 console.log("FoodList rendered");
3 return (
4 <ul>
5 {foodList.map((item) => (
6 <FoodItem
7 key={item.id}
8 item={item}
9 removeItem={removeItem}
10 />
11 ))}
12 </ul>
13 );
14 };

FoodItem
1 const FoodItem = ({ item, removeItem }) => {
2 console.log("FoodItem rendered");
3 return (
4 <>
5 <li>{item.name}</li>
6 <button
7 onClick={() => removeItem(item.id)}>
8 Remove :(
9 </button>
10 </>
11 );
12 };

Escribe en el input text y oberva los console logs desde la herramienta donde se ejecuta el código,
no de la consola de tu navegador.
En este link⁵ lo puedes ejecutar.
Como puedes ver, se hacen re renders en los componentes debido a que cambia la variable de
estado que usmamos para controlar el input, aunque realmente no cambien los valores de la lista de
alimentos.
El primer cambio que haremos es envolver los componentes FooList y FoodItem con React.memo
para que no se re rendericen dados los mismos props.
Nota: hacemos import de memo del modo: import React, { memo } from 'react';
⁵https://codesandbox.io/s/usecallback-9l0do

Ebook gratuito por Developero


Hook para memorizar funciones useCallback 22

FoodList con memo

1 const FoodList = memo(({ foodList, removeItem }) => {


2 console.log("FoodList rendered");
3 return (
4 <ul>
5 {foodList.map((item) => (
6 <FoodItem
7 key={item.id}
8 item={item}
9 removeItem={removeItem} />
10 ))}
11 </ul>
12 );
13 });

1 const FoodItem = memo(({ item, removeItem }) => {


2 console.log("FoodItem rendered");
3 return (
4 <>
5 <li>{item.name}</li>
6 <button
7 onClick={() => removeItem(item.id)}>
8 Remove :(
9 </button>
10 </>
11 );
12 });

Haz este cambio en el link del código que compartí anteriormente.


Si escribes de nuevo en el input, ¡verás que se siguen re renderizando todos los componentes! ¿Por
qué?
La razón es que React considera que el prop removeItem no es equivalente al anterior prop a través
de los re renderizados.
¿Recuerdas la parte de este capítulo donde está el ejemplo de la igualdad referencial?
Pondré la parte importante de este ejemplo:

1 () => {} === () => {} // false

Para resolver esto ahora pasamos a usar useCallback :D

Ebook gratuito por Developero


Hook para memorizar funciones useCallback 23

Dentro de FoodContainer

1 const removeItem = useCallback(


2 (id) => setFoodList(foodList.filter((foodItem) =>
3 foodItem.id !== id)),
4 [foodList]
5 );

El componente completo luce de este modo:

1 const FoodContainer = () => {


2 console.log("FoodContainer rendered");
3
4 const [foodList, setFoodList] = useState(food);
5 const [textInput, setTextInput] = useState("");
6
7 const handleChange = ({ target }) =>
8 setTextInput(target.value);
9
10 const removeItem = useCallback(
11 (id) => setFoodList(foodList.filter((foodItem) =>
12 foodItem.id !== id)),
13 [foodList]
14 );
15
16 return (
17 <>
18 <h2>My Food List</h2>
19 <p>
20 New food <input
21 value={textInput}
22 onChange={handleChange} />
23 </p>
24 <FoodList
25 foodList={foodList}
26 removeItem={removeItem} />
27 </>
28 );
29 };

Usando en React.memo y el hook useCallback ahora hemos logrado optimizar los re renderizados
de este ejemplo.

Ebook gratuito por Developero


Hook para memorizar funciones useCallback 24

A decir verdad, este es un ejemplo semi forzado porque la estructura de los componentes es candidata
a ser re factorizada para no tener la necesidad de pasar todos los props en el árbol de componentes.
Pero he querido hacerlo de este modo para poder ilustrar el ejemplo lo más fácil posible.

¿Debo memorizar funciones todo el tiempo?


No. Es muy importante elegir sabiamente en base a la relación costo - beneficio.
El costo por memorizar funciones es que agregamos más sentencias de código que necesitarán
memoria y que además hacen nuestro código un poco verboso.
React es realmente muy rápido en los renderizados.
Probablemente notarás mejoras en el performance cuando tengas una gran cantidad de componentes
cuyo costo por un re renderizado implique una gran cantidad de cálculos.
Sin emmargo, es bueno que conozcas useCallback para que lo tengas en cuenta en tus aplicaciones.

Recapitulacion del hook useCallback


• Usa este hook en las funciones que pases por props a componentes hijos que ocasionen re
renderizados innecesarios.
• Si la función depende de otras variables, pásalas en el array de dependencias de este hook.
• Puedes usar React.memo en conjunto para complementar una optimización de re renders.
• NO (en mayúsculas) necesitas aplicar useCallback en cada función que definas ya que tiene
un costo. Hazlo sólo cuando realmente exista una mejora significativa en el performance.

Este ha sido un hook interesante, ¿No lo crees?.


Ahora vamos al siguiente capítulo donde veremos una segunda estrategia de optimización más
interesante aún.

Ebook gratuito por Developero


Hook para optimizar cálculos
useMemo
El hook useMemo sirve para memorizar valores.
Recibe por parámetro una función para crear un valor a memorizar y por segundo parámetro un
array de dependencias.
Ejemplo de useMemo de reactjs.org

1 const memoizedValue = useMemo(() =>


2 computeExpensiveValue(a, b), [a, b]);

Es especialmente útil cuando el valor a memorizar es producto de un cálculo que consume “mucha”
memoria y procesamiento.
Ya que “mucha” tiende a ser subjetivo, considera la complejidad de un cálculo (o un algoritmo) en
base a lo siguiente:

• Constant: O(1)
• Logarithmic: O(log n)
• Linear: O(n)
• Linearithmic: O(n log n)
• Quadratic: O(n²)
• Expontential: O(2^n)
• Factorial: O(n!)

Un ejemplo clásico de una función que tenga una complejidad cuadrática (O(n²)) es cuando tienes
un ciclo dentro de otro ciclo.
functión de complejidad cuadrática

1 const expensiveCalc = (myArray) => {


2 for (let i = 0; i < myArray.length; i++) {
3 for (let j = 0; j < myArray.length; j++) {
4 // code...
5 }
6 }
7 }
Hook para optimizar cálculos useMemo 26

La pregunta es: ¿Cuántas veces se ejecutará el código dentro del segundo for si pasamos un
array con 10,000 elementos?
La respuesta es: 10,000 al cuadrado.
Ahora imagina que tienes algo como esto en un componente:

1 const expensiveCalc = (myArray) => {


2 let c = 0;
3
4 for (let i = 0; i < myArray.length; i++) {
5 for (let j = 0; j < myArray.length; j++) {
6 c = c + 1;
7 }
8 }
9
10 console.log('iteraciones:', c);
11 }
12
13 const MyComponent = ({ someList }) => {
14 const result = expensiveCalc(someList);
15
16 return <p>resutl: {result}</p>
17 }

Prueba ejecutar lo anterior dando click aquí⁶ y observa la consola de la plataforma (no la de tu
navegador).
Probablemente no tengas problemas en un inicio dependiendo de lo que necesites lograr en tu
componente.
Puede que en un inicio no tengas muchos valores en tu array.
Incluso puede que logres optimizar la complejidad de ese algoritmo usando otra estructura de datos
como un diccionario o una tabla hash.
Pero si quieres una solución basado en lo que tiene React, en este caso el hook useMemo viene como
anillo al dedo.
¿Ya probaste el código⁷ anterior? Si no lo has hecho, te animo a que lo hagas porque ahora vamos a
ver la manera de optimizarlo con useMemo.

⁶https://codesandbox.io/s/usememo-rb185?file=/src/App.js
⁷https://codesandbox.io/s/usememo-rb185?file=/src/App.js

Ebook gratuito por Developero


Hook para optimizar cálculos useMemo 27

Ejemplo de useMemo para optimizar un cálculo

1 const MyComponent = ({ someList }) => {


2 // Cambio aquí
3 const result = useMemo(() =>
4 expensiveCalc(someList), [someList]);
5
6 return <p>Iteraciones: {result.toLocaleString()}</p>;
7 };

Así de simple. Sólo cambiando una línea de código hemos logrado hacer una optimiación de algo
que potencialmente puede afectar el performance de nuestra aplicación.
Prueba el código actualizado dando click aquí⁸.

PureComponent, React.memo y useMemo


Primero vamos a definir la identidad de cada elemento:

• PureComponent es una API para declarar componentes de tipo clase.


• React.memo es un HOC (Componente de orden superior por sus siglas en inglés).
• useMemo es un hook del core de React.

No podemos hacer ejemplos de equivalencia entre estos elementos porque sería como comparar
peras con manzanas.
Lo que podemos hacer es observar cómo funcionan cada uno de ellos y es lo que vamos a hacer.

Para quien no esté familiarizado, PureComponent es parecido a Component que usamos para definir
componentes de tipo clase (ambos exportados en el import de React).
PureComponent es usado para componentes que al tener los mismos props y state, renderiza el
mismo resultado.
Implementa shouldComponentUpdate internamente, por lo que dados los mismos props y state, el
componente va a renderizar el mismo output.
Considera lo siguiente:

⁸https://codesandbox.io/s/usememo-aplicado-9otsj?file=/src/App.js:437-605

Ebook gratuito por Developero


Hook para optimizar cálculos useMemo 28

1 class Title extends PureComponent {


2 render() {
3 return <h1>{this.props.value}</h1>;
4 }
5 }
6
7 // <Title value="React Hooks" />

Este es un componente que sólo renderiza un elemento visual, no tiene lógica de llamada a api ni
nada por el estilo.
En este caso es conveniente usar PureComponent debido a que nos ahorra hacer la comparación
manualmente en si debe o no renderizar.
Su equivalente en Component usando shouldComponentUpdate es:

1 class Title extends Component {


2 shouldComponentUpdate(nextProps) {
3 // retorna true si son diferentes
4 // si son diferentes, renderiza de nuevo
5 return nextProps.value !== this.props.value;
6 }
7
8 render() {
9 return <h1>{this.props.value}</h1>;
10 }
11 }

Aunque sigue siendo preferible usar PureComponent, no sólo por ahorrarnos líneas de código
sino por la propia recomendación de la (documentación en React)[https://es.reactjs.org/docs/react-
api.html#reactpurecomponent].
En esta solución basta con crear un componente de tipo clase con PureComponent y React hace todo
el trabajo por nosotros.

El HOC React.memo nos permite optimizar los renderizados sólo cuando los props cambian.
Consideremos el componente Title en forma de componente funcional.

1 const Title = (value) => <h1>{value}</h1>;

Ahora vamos a hacer el wrapper con React.memo para optimizar su renderizado.

Ebook gratuito por Developero


Hook para optimizar cálculos useMemo 29

1 const Title = (value) => <h1>{value}</h1>;


2
3 const TitleWithMemo = React.memo(Title);
4
5 // <TitleWithMemo value="memo" />

Y con esto es suficiente.

Para useMemo ya hemos visto un ejemplo al principio.


Lo importante a destacar aquí es que este hook nos va a ayudar a memorizar valores dentro de un
componente.

Recapitulacion del hook useMemo


En este capítulo hemos visto lo siguiente:

• El hook useMemo nos sirve para memorizar valores.


• Recibe una función que retorna el valor a memorizar y como segundo parámetro opcional, un
array de dependencias.
• Si no pasamos el array de dependencias, se ejecutará en cada renderizado.
• Especialmente útil para ahorrar cálculos costosos en cada renderizado como vimos en el
ejemplo práctico.
• PureComponet nos crea un componente que dados los mismos props y estado, ahorra el
renderizado.
• React.memo es un HOC que permite optimizar un componente ahorrando los renderizados al
tener los mismos props.

Con esto hemos cubierto gran parte del tema para hacer optimizaciones en componentes en React.
Si aún no has leído el capítulo de useCallback te recomiendo que lo hagas para tener un
complemento de este capítulo.
Si ya lo leíste, ahora te invito a que pasemos al siguiente :D

Ebook gratuito por Developero


Hook para estados complejos
useReducer
Este capítulo va a abarcar varios conceptos que si no estás familiarizado con ellos, puede que te
sientas un poco abrumado.
Pero descuida, he hecho mi mejor esfuerzo para ponertela fácil y que al final del capítulo logres
dominar este hook.
Si ya estás familiarizado con redux, será bastante simple de entender y siéntete libre de bricanr hasta
la parte del ejemplo de useReducer.
Dicho lo anterior, comencemos :).
El hook useReducer nos permite manejar estados “complejos” por medio de una función reductora.
¿Y qué es un estado “complejo”?
Un estado complejo es aquel que tiene una estructura de varios niveles, por ejemplo: un objeto de
objetos que contiene arrays.
Ejemplo de un estado complejo

1 {
2 foo: {
3 faa: {
4 test: [],
5 testB: ''
6 }
7 },
8 fee: [],
9 fii: {
10 testC: [],
11 testD: {}
12 }
13 }

En caso de que requieras un estado como el ejemplo anterior y no sea viable dividirlo usando
diferentes useState, lo recomendado es que uses useReducer.
¿Y cómo luce este hook?
Hook para estados complejos useReducer 31

Ejemplo de useReducer de reactjs.org

1 const [state, dispatch] = useReducer(


2 reducer,
3 initialArg,
4 init
5 );

Donde reducer es simplemente una función de Javascript que actualiza el estado, initialArg es el
estado inicial e init es una función opcional para también definir el estado inicial.
Este hook devuelve un array donde en la posición cero es el estado y en la posición uno es un
dispatch que es una función que usaremos para actualizar el estado.

En realidad, es muy parecido al hook useState:

1 const initialState = false;


2 const [state, setState] = useState(initialState);

Veamos más sobre el hook useReducer haciendo una comparación con redux y ejemplos sobre su
uso en los siguientes apartados.

Funciones reductoras (reducers)


Un reducer es una función de Javasctipt que recibe por parámetro el valor del estado actual y por
segundo parámetro un objeto plano de Javascript llamado action con la información necesaria para
actualizar el estado.
“Kha?”
Suena más complejo de lo que parece. Mira el siguiente ejemplo de un reducer:
Ejemplo de un reducer de reactjs.org

1 function reducer(state, action) {


2 switch (action.type) {
3 case 'increment':
4 return {count: state.count + 1};
5 case 'decrement':
6 return {count: state.count - 1};
7 default:
8 throw new Error();
9 }
10 }

Ebook gratuito por Developero


Hook para estados complejos useReducer 32

Como podemos ver, el reducer recibe el valor del estado y un action.


El parámetro action es un objeto plano de Javascript y tendrá por lo menos una propiedad llamada
type que usamos como una cadena, ejemplo: { type: 'ADD_TODO' }.

Según el valor de action.type, es el cambio de estado a ejecutar.


Es importante que sepas que no mutamos el estado sino que creamos y devolvemos uno nuevo.
Hay quienes prefieren evitar el uso de switch pero debes saber que es opcional usarlo. Puedes usar
una serie de sentencias if también. Depende de ti.
Este ejemplo de la documentación oficial de reactjs.org es sólo una muestra de cómo podría lucir un
reducer para un estado de un contador, pero es sólo para fines de ejemplo.
Al inicio comentamos que useReducer es recomendado para estados complejos y es lo que veremos
en el ejemplo práctico.

Ejemplo de un componente con useReducer


Vamos a hacer un componente para manejar una lista de cosas por hacer (TODO list).
Antes de implementar useReducer primero vamos a definir el estado inicial y la función reductora
que nos pide este hook al ejecutarlo.
Primero hay que pensar en cuál es el estado más simple que puede necesitar nuestro componente de
cosas por hacer.
Consideremos lo siguiente:

• El usuario va a poder agregar nuevos elementos en la lista de cosas por hacer.


• El usuario necesita visualizar en la UI la lista de las cosas por hacer.
• El usuario va a poder marcar como completado o no completado un elemento de lista.

Te propongo que usemos la siguiente estructura:


Estado de TODO list

1 const state = [
2 {
3 id: 1,
4 name: "Terminar de leer el capítulo de useReducer",
5 isCompleted: false
6 }
7 ];

Ebook gratuito por Developero


Hook para estados complejos useReducer 33

Un array de objetos planos en el que cada objeto tendrá un id para identificarlo (podríamos usar el
índice del array pero no es lo común en aplicaciones reales), un name y un booleano isCompleted
que usaremos para marcar como completado o no completado.
Ahora hagamos nuestro reducer incluyendo la lógica necesaria para agregar un nuevo elemento de
nuestro TODO solamente de momento.
Reducer con lógica para agregar un TODO

1 const reducer = (state, action) => {


2 if (action.type === "ADD_TODO") {
3 const { name } = action.payload;
4
5 return [
6 ...state,
7 {
8 id: uuidv4(),
9 name,
10 isCompleted: false
11 }
12 ];
13 }
14 };

Como es común, el reducer recibe el state y action. Estos valores serán pasados internamente por
useReducer.

Lo que nos debemos preocupar aquí es en colocar la lógica para actualizar el estado, cosa que
hacemos en el primer if.
Asumimos que tendremos un action.type igual a "ADD_TODO" y cuando lo recibamos vamos a
retornar el nuevo estado.
Para retornar el nuevo estado, vamos a usar el spread operator para crear un nuevo array agregando
todos los valores actuales de state, ya que state es un array.
Después agregamos el nuevo objeto con el id (usamos una biblioteca llamada uuid para que nos
genere los id únicos), el name que proviene de action.payload e isCompleted como false por default.
Ahora vamos a agregar la lógica para actualizar isCompleted de un elemento del TODO en particular.
Veremos únicamente la parte correspondiente dentro del reducer.

Ebook gratuito por Developero


Hook para estados complejos useReducer 34

Lógica para actualizar isCompleted de un elemento de la lista


1 // dentro del reducer, este IF nuevo
2 if (action.type === "TOGGLE_IS_COMPLETED") {
3 const { id } = action.payload;
4
5 const newState = state.map((singleTodo) => {
6 if (singleTodo.id === id) {
7 return {
8 ...singleTodo,
9 isCompleted: !singleTodo.isCompleted
10 };
11 }
12
13 return singleTodo;
14 });
15
16 return newState;
17 }

Esta lógica es muy diferente a la de agregar. No hacemos spread ni nada por el estilo.
Primero obtenemos el id del elemento a actualizar por medio de action.payload, después creamos
el nuevo estado por medio de state.map (.map retorna un nuevo array).
Dentro de .map está la parte importante.
Lo que hacemos aquí es comparar elemento por elemento para saber si el elemento de la lista coincide
con el id que nos interesa actualizar.
Si coincide, retornamos todo lo que contenga ese elemento de la lista y su propiedad isCompleted le
asignamos el valor contrario que tenga en ese momento.
Ejemplo:

1 // Si...
2 singleTodo.isCompleted = true
3
4 // Entonces...
5 !singleTodo.isCompleted // igual a false
6
7 // y viceversa
8
9 // Si...
10 singleTodo.isCompleted = false
11

Ebook gratuito por Developero


Hook para estados complejos useReducer 35

12 // Entonces...
13 !singleTodo.isCompleted // igual a true

Si no coincide con el id, simplemente retornamos el elemento sin modificarlo.


Al final retornamos el nuevo estado con su valor modificado.
Nuestro reducer completo luce del siguiente modo:
reducer completo
1 const reducer = (state, action) => {
2 if (action.type === "ADD_TODO") {
3 const { name } = action.payload;
4
5 return [
6 ...state,
7 {
8 id: uuidv4(),
9 name,
10 isCompleted: false
11 }
12 ];
13 }
14
15 if (action.type === "TOGGLE_IS_COMPLETED") {
16 const { id } = action.payload;
17
18 const newState = state.map((singleTodo) => {
19 if (singleTodo.id === id) {
20 return {
21 ...singleTodo,
22 isCompleted: !singleTodo.isCompleted
23 };
24 }
25
26 return singleTodo;
27 });
28
29 return newState;
30 }
31
32 return state;
33 };

Ahora que tenemos estos elementos definidos, pasemos a crear nuestro componente :)

Ebook gratuito por Developero


Hook para estados complejos useReducer 36

Componente sin reducer aún

1 const Todo = () => {


2 const [todoText, setTodoText] = useState("");
3
4 const handleChange = ({ target }) =>
5 setTodoText(target.value);
6
7 return (
8 <>
9 <p>
10 Nuevo TODO:
11 <input
12 type="text"
13 value={todoText}
14 onChange={handleChange}
15 />
16 <button>Agregar</button>
17 </p>
18
19 <h2>Listado</h2>
20 <ul>
21 <li>Test</li>
22 </ul>
23 </>
24 );
25 };

Tenemos el input para escribir el nuevo TODO y lo controlamos con un estado usando useState.
El listado está “hardcodeado” sólo para tener una idea de cómo mostraremos los elementos.
Ahora pasemos a agregar el useReducer :).

1 // dentro del componente agregamos:


2 const [todoText, setTodoText] = useState("");
3
4 // AQUI
5 const [state, dispatch] = useReducer(reducer, initialState);
6
7 // ...resto del código

Y modificamos la parte del render para que muestre los valores de nuestro state. Recuerda que
pusimos un valor inicial en las cosas por hacer, por lo que será lo que se renderice en la lista.

Ebook gratuito por Developero


Hook para estados complejos useReducer 37

renderizar listado (dentro del return del componente)

1 <ul>
2 {
3 state.map(({ name, isCompleted, id }) => {
4 const style = {
5 textDecoration: isCompleted
6 ? "line-through" : "inherit"
7 };
8
9 return (
10 <li key={id} style={style}>
11 {name}
12 </li>
13 );
14 })
15 }
16 </ul>

De momento, nuestro componente luce del siguiente modo:

1 const Todo = () => {


2 const [todoText, setTodoText] = useState("");
3 const [state, dispatch] = useReducer(reducer, initialState);
4
5 const handleChange = ({ target }) =>
6 setTodoText(target.value);
7
8 return (
9 <>
10 <p>
11 Nuevo TODO:
12 <input
13 type="text"
14 value={todoText}
15 onChange={handleChange}
16 />
17 <button>Agregar</button>
18 </p>
19
20 <h2>Listado</h2>
21
22 <ul>

Ebook gratuito por Developero


Hook para estados complejos useReducer 38

23 {state.map(({ name, isCompleted, id }) => {


24 const style = {
25 textDecoration: isCompleted
26 ? "line-through" : "inherit"
27 };
28
29 return (
30 <li key={id} style={style}>
31 {name}
32 </li>
33 );
34 })}
35 </ul>
36 </>
37 );
38 };

Ahora vamos a colocar la lógica para que al dar click en el botón “Agregar”, nos agregue el nuevo
elemento en nuestro estado.

1 const handleClick = () => {


2 dispatch({
3 type: "ADD_TODO",
4 payload: { name: todoText }
5 });
6 setTodoText("");
7 };
8
9 // en el botón
10 <button onClick={handleClick}>Agregar</button>

Ejecutamos el dispatch para actualizar el estado. Le pasamos el type y el payload necesarios como
se ve en el ejemplo.
Al final, reseteamos el valor de todoText para limpiar el contenido del input.
Por último, vamos a agregar la capacidad de marcar un elemento como completado o no completado
al dar click a un elemento del listado.

Ebook gratuito por Developero


Hook para estados complejos useReducer 39

1 const handleToggle = (id) => {


2 dispatch({
3 type: "TOGGLE_IS_COMPLETED",
4 payload: { id }
5 });
6 };
7
8 // en el elemento li
9 return (
10 <li
11 key={id}
12 style={style}
13 onClick={() => handleToggle(id)}
14 >
15 {name}
16 </li>
17 );

El código completo luce del siguiente modo:


Ejemplo completo de useReducer

1 const Todo = () => {


2 const [todoText, setTodoText] = useState("");
3 const [state, dispatch] = useReducer(reducer, initialState);
4
5 const handleChange = ({ target }) =>
6 setTodoText(target.value);
7
8 const handleClick = () => {
9 dispatch({
10 type: "ADD_TODO",
11 payload: { name: todoText }
12 });
13 setTodoText("");
14 };
15
16 const handleToggle = (id) => {
17 dispatch({
18 type: "TOGGLE_IS_COMPLETED",
19 payload: { id }
20 });
21 };
22

Ebook gratuito por Developero


Hook para estados complejos useReducer 40

23 return (
24 <>
25 <p>
26 Nuevo TODO:
27 <input
28 type="text"
29 value={todoText}
30 onChange={handleChange}
31 />
32 <button onClick={handleClick}>Agregar</button>
33 </p>
34
35 <h2>Listado</h2>
36
37 <ul>
38 {state.map(({ name, isCompleted, id }) => {
39 const style = {
40 textDecoration: isCompleted
41 ? "line-through" : "inherit"
42 };
43
44 return (
45 <li
46 key={id}
47 style={style}
48 onClick={() => handleToggle(id)}
49 >
50 {name}
51 </li>
52 );
53 })}
54 </ul>
55 </>
56 );
57 };

Puedes ejecutar y editar el código fuente en este link⁹


Con esto hemos terminado nuestro ejemplo :D
⁹https://codesandbox.io/s/usereducer-d8r7z?file=/src/App.js:804-1826

Ebook gratuito por Developero


Hook para estados complejos useReducer 41

Diferencia entre useReducer y Redux


El nombre de useReducer da pie a confundirlo con redux, pero en realidad son cosas completamente
diferentes.

• useReducer es un hook de React para actualizar un estado interno por medio de una función
llamada reducer.
• redux es una arquitectura que nos permite abstraer el manejo de un estado global en una
aplicación.

Redux es algo más complejo que abarca el nivel de decisiones de arquitectura de una aplicación,
siendo react-redux una biblioteca que nos permite integrarlo fácilmente en React.
La confusión puede caer en el uso de funciones llamadas reducers que son usadas como una parte
del funcionamiento de redux.

Recapitulacion del hook useReducer


En este capítulo hemos visto lo siguiente:

• Los conceptos de reducer, action y su funcionamiento con useReducer.


• Un ejemplo práctico de un TODO aplicando useReducer.
• Diferencias entre redux y useReducer.

Espero que este capítulo te haya dado luces para que puedas comenzar a implementar este hook en
tus desarrollos cuando lo consideres necesario.
Te veo en el siguiente capítulo.

Ebook gratuito por Developero


Hook para manejar la API de Context
useContext
El hook useContext nos sirve para poder implementar la API de Context que ya existía en React
desde antes de los hooks.
Ejemplo de useContext de reactjs.org

1 const value = useContext(MyContext);

Para quien no esté familiarizado, Context nos permite comunicar props en un árbol de componentes
sin necesidad de pasarlos manualmente a través de props.
Algunos ejemplos de cuándo utilizar Context son:

• Un Tema de la UI (light theme, dark theme, etc).


• Autenticación del usuario.
• Idioma preferido.

Pero no es limitado a los ejemplos anteriores. Puedes aplicar Context a las necesidades de tu
aplicación según tu criterio.
En otras palabras, no es obligatorio que Context maneje un “estado global” de la aplicación: Puede
abarcar sólo una parte del estado si lo deseas.
A continuación vamos a ver ejemplos y comparaciones de cómo utilizar este hook para que puedas
aprovechar todo su potencial.

Ejemplo de Context en componente clase y


useContext en componente funcional
Primero implementaremos un Context para aplicar un tema en componentes usando la API
tradicional y luego lo refactorizaremos usando useContext.
Esto es sólo para poder identificar las semejanzas y diferencias entre ambas opciones. No quiere
decir que la manera tradicional sea obsoleta ni nada por el estilo.
Dicho lo anterior, comenzaremos creando un Context para manejar el tema del siguiente modo:
Hook para manejar la API de Context useContext 43

1 // theme-context.js
2
3 export const themes = {
4 light: {
5 color: "#555555",
6 background: "#eeeeee"
7 },
8 dark: {
9 color: "#eeeeee",
10 background: "#222222"
11 },
12 vaporwave: {
13 color: "#ffffff",
14 background: "#ff71ce"
15 }
16 };
17
18 export const ThemeContext = React.createContext(themes.light);

En este caso tenemos un objeto para representar temas diferentes que podemos usar en nuestra UI.
El tema por defecto es themes.light que es pasado por parámetro a createContext.
Ahora necesitamos proveer este context a nuestro árbol de componentes:
Proveer Context
1 export default function App() {
2 const [currentTheme, setCurrentTheme] =
3 useState(themes.light);
4
5 return (
6 <div className="App">
7 <h1>React Context</h1>
8
9 <ThemeContext.Provider value={currentTheme}>
10 <MyButton>Hello World!</MyButton>
11 </ThemeContext.Provider>
12 </div>
13 );
14 }

Estamos creando una variable de estado con useState para obtener el valor actual del tema.
Usamos ThemeContext.Provider value={currentTheme} para asignar el context y a partir de aquí,
todo el árbol de componentes hijos van a poder tener acceso a context si lo desean.

Ebook gratuito por Developero


Hook para manejar la API de Context useContext 44

Para usarlo en un componente lo hacemos del siguiente modo:


Consumir Context

1 class MyButton extends Component {


2 render() {
3 const theme = this.context;
4 const style = {
5 backgroundColor: theme.background,
6 color: theme.color,
7 border: "1px solid",
8 borderRadius: 5
9 };
10 return <button style={style} {...this.props} />;
11 }
12 }
13
14 // Esto es importante (asignar el context)
15 MyButton.contextType = ThemeContext;

Y con esto ya tenemos nuestro context funcionando.


Ahora si queremos modificar el context, en este ejemplo basta con hacer lo que sigue:
modificar context

1 export default function App() {


2 const [currentTheme, setCurrentTheme] = useState(themes.light);
3
4 return (
5 <div className="App">
6 <h1>React Context</h1>
7 <ThemeContext.Provider value={currentTheme}>
8 <MyButton
9 onClick={() => setCurrentTheme(themes.dark)}
10 >
11 Dark Theme
12 </MyButton>
13 <MyButton
14 onClick={() => setCurrentTheme(themes.vaporwave)}
15 >
16 Vaporwave Theme
17 </MyButton>
18 </ThemeContext.Provider>
19 </div>

Ebook gratuito por Developero


Hook para manejar la API de Context useContext 45

20 );
21 }

Con esto ya funciona pero por lo común vas a tener la necesidad de modificar el context desde un
componente hijo.
Para eso podemos modificar nuestro context para proveer su valor y además una función que sirva
para modificar su valor.
Primero modificamos nuestro context:

1 export const ThemeContext = React.createContext({


2 theme: themes.light,
3 updateTheme: () => {}
4 });

Ahora tendrá un objeto con las dos propiedades antes dichas.


En donde hacemos uso de Provider necesitamos modificar también su valor:

1 export default function App() {


2 const [currentTheme, setCurrentTheme] = useState(themes.light);
3
4 return (
5 <div className="App">
6 <h1>React Context</h1>
7
8 {/* Nuevo value definido */}
9 <ThemeContext.Provider
10 value={{
11 theme: currentTheme,
12 updateTheme: setCurrentTheme
13 }}
14 >
15 <MyButton
16 onClick={() => setCurrentTheme(themes.dark)}
17 >
18 Dark Theme
19 </MyButton>
20
21 <MyButton
22 onClick={() => setCurrentTheme(themes.vaporwave)}
23 >
24 Vaporwave Theme

Ebook gratuito por Developero


Hook para manejar la API de Context useContext 46

25 </MyButton>
26
27 {/* Nuevo Boton sin onClick */}
28 <MyButton>Light Theme</MyButton>
29 </ThemeContext.Provider>
30 </div>
31 );
32 }

En el componente MyButton vamos a hacer que si no recibe el prop de onClick, pondremos por
default updateTheme que obtenemos de ThemeContext:

1 class MyButton extends Component {


2 render() {
3 const { theme, updateTheme } = this.context;
4 const style = {
5 backgroundColor: theme.background,
6 color: theme.color,
7 border: "1px solid",
8 borderRadius: 5
9 };
10
11 const updateLightTheme = () => {
12 updateTheme(themes.light);
13 };
14
15 const onClick = this.props.handleClick ||
16 updateLightTheme;
17
18 return <button
19 onClick={onClick}
20 style={style}
21 {...this.props}
22 />;
23 }
24 }

Con esto ya estamos actualizando context desde un componente hijo o de manera directa en el
componente donde hacemos el provider.

Ebook gratuito por Developero


Hook para manejar la API de Context useContext 47

Ahora vamos a hacer una nueva versión del código anterior para hacer uso de useContext y puedas
apreciar las diferencias.
La definición de ThemeContext no necesitamos modificarla, ni el Provider.
Lo que nos va a servir useContext es para poder acceder al valor de un context en un componente
funcional.
Veamos el refactor:
Ejemplo de useContext
1 const MyNewButton = (props) => {
2 const { theme, updateTheme } = useContext(ThemeContext);
3
4 const style = {
5 backgroundColor: theme.background,
6 color: theme.color,
7 border: "1px solid",
8 borderRadius: 5
9 };
10
11 const updateLightTheme = () => {
12 updateTheme(themes.light);
13 };
14
15 const onClick = props.handleClick || updateLightTheme;
16
17 return <button onClick={onClick} {...props} style={style} />;
18 };

Como puedes ver, lo único que cambia es que ahora podemos obtener nuestro valor de context
usando const { theme, updateTheme } = useContext(ThemeContext);.
Con esto cubrimos el tema de Context en sí mismo y el hook useContext.

Recapitulacion del hook useContext


En este capítulo hemos visto:

• Cómo es el uso básico api de Context.


• Algunos casos de uso en los cuáles puedes aplicar Context.
• Una manera de aplicar Context en componente de tipo clase.
• La manera de aplicar Context con el hook useContext.

Espero que este capítulo te haya sido de utilidad.

Ebook gratuito por Developero


Hook para trabajar con referencias
useRef
El hook useRef nos permite trabajar con referencias en componentes funcionales.
Ejemplo de useRef de reactjs.org
1 const refContainer = useRef(initialValue);

Este hook nos retorna un objeto con una propiedad current que es mutable y cuyo valor persiste
durante los renderizados y ciclo de vida del componente.
Un caso de uso es usar referencias al DOM o a componentes de React.
Por ejemplo, cuando queremos hacer que un input tenga un auto focus al montarse.
Primero vamos a hacer el ejemplo con un componente de tipo clase y posteriormente la versión con
useRef.

Ejemplo de autofocus en componente clase


1 class MyForm extends Component {
2 inputEl = null;
3
4 componentDidMount() {
5 this.inputEl.focus();
6 }
7
8 render() {
9 return (
10 <p>
11 Name:
12 <input
13 type="text"
14 ref={(el) => (this.inputEl = el)}
15 />
16 </p>
17 );
18 }
19 }

Usando useRef queda del siguiente modo:


Hook para trabajar con referencias useRef 49

Ejemplo de auto focus con useRef

1 const Form = () => {


2 const inputEl = useRef(null);
3
4 useEffect(() => inputEl.current.focus(), []);
5
6 return (
7 <p>
8 Name:
9 <input type="text" ref={inputEl} />
10 </p>
11 );
12 };

Puedes ejecutar y editar el este código en este enlace¹⁰.


Otra característica importante de ref es que si su valor cambia, no causa un re render.

Emular variables de instancia en componente


funcional (this)
Dentro de un componente de tipo clase podemos crear variables de instancia como el siguiente
ejemplo:

1 class MyComponent extends React.Component {


2 intervalId = null;
3
4 componentDidMount() {
5 // Accedemos con "this"
6 this.intervalId = setInterval(() => {
7 // ...
8 });
9 }
10
11 componentWillUnmount() {
12 clearInterval(this.intervalId);
13 }
14
15 render () {
¹⁰https://codesandbox.io/s/useref-d6k4u

Ebook gratuito por Developero


Hook para trabajar con referencias useRef 50

16 // code
17 }
18 }

El hook useRef también nos sirve para tener referencias a cualquier valor.
De este modo, podemos emular variables de instancia:
Ejemplo de reactjs.org

1 function Timer() {
2 const intervalRef = useRef();
3
4 useEffect(() => {
5 const id = setInterval(() => {
6 // ...
7 });
8 intervalRef.current = id;
9 return () => {
10 clearInterval(intervalRef.current);
11 };
12 });
13
14 // ...
15 }

Recapitulacion del hook useRef


Este ha sido un capítulo corto debido a la sencillez de este hook. Lo que vimos fue:

• Cómo usar ref en un componente de tipo clase.


• Cómo usar ref en un componente de tipo función usando useRef para elementos de la UI.
• Cómo usar useRef para crear valores que se comporten parecido a como funciona una variable
de instancia de una clase.

Ebook gratuito por Developero


Siguientes pasos en tu carrera de
React Developer
¡Muchas felicidades por haber terminado este ebook! :D
Hemos aprendido cómo funcionan los principales hooks, los cómo, por qué y en qué casos podemos
aplicarlos.

¿Te quedaste con ganas de más?


Este ebook cubre las bases iniciales para poder pasar a un siguiente nivel de patrones avanzados
en React de los cuáles muchos de ellos son aplicados mediante hooks.
Si te gustaría pasar al siguiente nivel en tus conocimientos sobre React, te recomiendo que le des un
ojo a este ebook que publiqué en Amazon:

• Introducción a MERN¹¹. Crea una mini app usando uno de los stacks de Javascript más
populares usando MongoDB, Express, React y Node. Es una lista de vídeos gratuita.
• Patrones Avanzados en React JS: Crea componentes realmente reutilizables¹². Puedes leerlo
gratis si tienes la suscripción de Kindle Unlimited.
• Entra al sitio web de Developero¹³ para poder seguir más contenidos como este :)

Por último, quiero pedirte un favor. Si tienes posibilidad de dejar tu review y valoración de este
ebook gratuito, te lo voy a agradecer mucho :D

¹¹https://www.youtube.com/watch?v=p9RfYt6PGRc&list=PLkr7dGY4D2sOFts538pghxWaabulH2kMU&index=1&ab_channel=
Developero
¹²https://amzn.to/2Gos5A8
¹³https://developero.io/

También podría gustarte