Cómo utilizar la API de contexto de React: tutorial con ejemplos

Cómo utilizar la API de contexto de React: tutorial con ejemplos

En React, los datos se transmiten normalmente de un componente principal a un componente secundario a través de propiedades. Pero esto puede dar lugar a una “perforación de propiedades”, en la que tenemos que pasar propiedades a través de muchos componentes para que lleguen a donde se necesitan.

Además, algunos componentes dentro de una aplicación requerirán algunos accesorios (por ejemplo, el usuario autenticado actual, el tema de la interfaz de usuario o el idioma preferido).

La API Context de React ofrece una forma de compartir valores como estos entre componentes sin tener que pasarlos explícitamente como una propiedad a través de cada nivel del árbol. Por lo tanto, Context está diseñado para compartir datos que se pueden considerar “globales” para un árbol de componentes de React.

Lo que aprenderá en este artículo

Código fuente

Todos los ejemplos de este artículo están en este repositorio: https://github.com/DoableDanny/React-context-API-tutorial

También hice una versión en video de este artículo para que te sea más fácil seguir los ejemplos: Tutorial de contexto de React con ejemplos

¿Qué es la API de contexto de React y cuándo debería usarla?

La API de contexto es una función de React que proporciona una forma de compartir valores como temas, información de usuario o ajustes de configuración entre componentes sin tener que pasar explícitamente propiedades a través de cada nivel del árbol de componentes. Esto la hace particularmente útil para administrar el estado global o el estado que necesitan muchos componentes en diferentes niveles de anidamiento.

La API de contexto es parte de la biblioteca React, lo que significa que no es necesario instalarla como un paquete de terceros en una aplicación React.

Por lo tanto, la API de contexto se puede utilizar para compartir variables globales entre componentes en una aplicación React, sin tener que pasar estas variables como propiedades a lo largo del árbol de componentes. Esto es especialmente útil si hay componentes que están profundamente anidados y necesitan acceder a variables de componentes superiores.

Ahora, aprendamos cómo funciona la API de contexto revisando un ejemplo de caso de uso común para la API de contexto…

Ejemplo de API de contexto de React: tema de interfaz de usuario en modo claro y oscuro

Un caso de uso real muy común para la API de React Context es almacenar el tema preferido del usuario actual, es decir, “modo claro” o “modo oscuro”.

Piénsalo: muchos de los componentes de la interfaz de usuario de una aplicación React necesitarán conocer el tema actual para poder mostrar los estilos adecuados. Botones, encabezados, barra de navegación, pie de página, menús desplegables… muchos componentes necesitarán mostrarse de forma diferente según el tema actual.

La solución de transmitir un accesorio a sus descendientes

La forma más simple y obvia de “React” de resolver esto sería crear una themevariable en el componente principal de nivel superior Appy luego seguir pasándola como una propiedad a todos los componentes del árbol. Pero esto conduce a un problema de React conocido como “prop boring”.

Prop Drilling es un término que se utiliza en React para describir el proceso de pasar datos desde un componente principal a un componente secundario profundamente anidado a través de múltiples componentes intermediarios. Esto puede suceder cuando necesitas pasar estados o funciones a varios niveles del árbol de componentes.

Ejemplo de perforación de apoyo:


function App() {
  const theme = 'dark';
  return <Parent theme={theme} />;
}

function Parent({ theme }) {
  return <Child theme={theme} />;
}

function Child({ theme }) {
  return <Button theme={theme} />;
}

function Button({ theme }) {
  return <button style={{ background: theme === 'dark' ? 'black' : 'white' }}>Click me</button>;
}

Como puede ver, cada componente intermediario debe incluir la propiedad, incluso si no la usa, solo para pasarla a otros niveles. Esto complica el código y lo hace más difícil de entender.

Además, los componentes intermedios que no utilizan los accesorios pueden volver a renderizarse cuando estos cambian, lo que genera problemas de rendimiento. Esto puede ser particularmente problemático en aplicaciones grandes con árboles de componentes profundos.

La API de contexto al rescate

Podemos resolver este problema de perforación de la hélice utilizando la API de contexto.

Creando un contexto

Primero, necesitamos crear el contexto y pasar el tema claro como valor predeterminado:

// src/contexts/ThemeContext.js

import { createContext } from "react";

export const themes = {
  light: {
    background: "white",
    text: "black",
  },
  dark: {
    background: "black",
    text: "white",
  },
};

export const ThemeContext = createContext(themes.light);

Arriba, hemos creado una contextscarpeta dentro de nuestra srccarpeta para almacenar todos nuestros contextos. Se considera una buena práctica crear cada contexto en su propio archivo. En nuestro caso, solo necesitamos crear un contexto para almacenar el tema actual.

Tenga en cuenta que los contextos se crean llamando a la createContext()función que proviene de la Reactbiblioteca. Pasamos a la createContext()función un valor predeterminado de themes.light.

Proporcionar un contexto

A continuación, debemos incluir todos los componentes que necesitan acceso al tema en un proveedor de contexto. El proveedor de contexto acepta una valuepropiedad, donde podemos pasar el valor que queremos que sea global.

A continuación, <Navbar /><Button />tendrá acceso al themeestado, aunque no lo hayamos transmitido explícitamente como una propiedad. Esto se debe a que hemos incluido estos componentes en el proveedor de contexto del tema y le hemos pasado el valor ( theme) que debe hacerse global.

// src/App.js

import React, { useState } from "react"
import { ThemeContext, themes } from "./contexts/ThemeContext"
import Navbar from "./components/Navbar"
import Button from "./components/Button"

const App = () => {
  const [theme, setTheme] = useState(themes.light)

  const toggleTheme = () => {
    setTheme(state => (state === themes.light ? themes.dark : themes.light))
  }

  return (
    <div className="App">
      <ThemeContext.Provider value={theme}>
        <Navbar />
        <Button changeTheme={toggleTheme} />
      </ThemeContext.Provider>
    </div>
  )
}

export default App

Si también quisiéramos que estuviera setTheme()disponible en toda nuestra aplicación a través del contexto, podríamos pasar el siguiente objeto a la valuepropiedad. Entonces podríamos alternar el tema desde cualquier componente dentro del proveedor de contexto de tema:

<ThemeContext.Provider value={{ theme, setTheme }}>

Ahora, vamos a crear los componentes ButtonNavbarque consumirán el contexto del tema mediante el useContext()gancho. Observe cómo cambian los estilos CSS de los componentes según los valores del tema actual:

// src/components/Button.js

import React, { useContext } from "react"
import { ThemeContext } from "../contexts/themeContext"

const Button = ({ changeTheme }) => {
  const theme = useContext(ThemeContext)

  return (
    <button
      style={{ backgroundColor: theme.background, color: theme.text }}
      onClick={changeTheme}
    >
      Toggle theme
    </button>
  )
}

export default Button
// src/components/Navbar.js

import React, { useContext } from "react"
import { ThemeContext } from "../contexts/themeContext"

const Navbar = () => {
  const theme = useContext(ThemeContext)

  return (
    <nav style={{ backgroundColor: theme.background }}>
      <ul>
        <li style={{ color: theme.text }}>Home</li>
        <li style={{ color: theme.text }}>About</li>
      </ul>
    </nav>
  )
}

export default Navbar

Estos son los pasos necesarios para utilizar un contexto :

  1. Importa el contexto que quieres utilizar ( ThemeContexten este ejemplo) en el componente.
  2. Importar el useContextgancho desde React.
  3. Dentro del componente que necesita acceder a los valores de contexto, llama al useContextgancho y pasa el contexto que quieres usar. Asígnalo a una variable ( const theme = useContext(ThemeContext)en nuestro ejemplo)
  4. El componente ahora tiene acceso a la variable global y se volverá a representar/actualizará cada vez que se actualice un valor dentro del contexto.

Bien, eso es todo lo que necesitamos para este ejemplo. Ahora, iniciemos nuestra aplicación ejecutando el siguiente comando en la ruta del proyecto:

npm run start

Ahora probemos las cosas en el navegador.

Modo claro:

modo_luz

**Presione el botón Cambiar tema**

Modo oscuro:

modo oscuro

Y listo, hemos usado la API de contexto para compartir el estado del tema en toda nuestra aplicación, sin tener que pasarlo como un elemento adicional. ¡Genial! 👌

Cómo crear múltiples contextos de React

En nuestro ejemplo anterior, solo creamos un contexto, ThemeContext. Pero ¿qué sucede si tenemos otros datos que deben hacerse globales, como el usuario que ha iniciado sesión actualmente usernameage?

Podríamos simplemente crear un gran contexto para almacenar todas las variables que necesitan consumirse globalmente:

<OneBigContext.Provider value={{ theme, username, age }}>
  <Button changeTheme={toggleTheme} />
  <Navbar />
</OneBigContext.Provider>

Pero esto se considera una mala práctica, ya que cada vez que se actualiza un valor de contexto, todos los componentes que consumen ese contexto se volverán a renderizar. Esto significa que todos los componentes que solo necesitan conocer las themevariables de usuario y no las de usuario se volverán a renderizar cada vez que se actualice alguna de las variables de usuario. Esto puede empeorar el rendimiento de una aplicación, especialmente en aplicaciones más grandes con muchos componentes complejos.

Podemos resolver esto creando múltiples contextos (un contexto para el tema y otro para los datos del usuario) y envolviendo nuestra aplicación en ambos proveedores, de la siguiente manera:

<ThemeContext.Provider value={theme}>
  <UserContext.Provider value={{ username, age }}>
    <Button changeTheme={toggleTheme} />
    <Navbar />
  </UserContext.Provider>
</ThemeContext.Provider>

Al almacenar únicamente datos relacionados en cada contexto, ayudamos a evitar reproducciones innecesarias de componentes y mejoramos el rendimiento de nuestra aplicación.

Cómo evitar el problema de volver a renderizar el contexto en React

Como hemos comentado, siempre que se actualiza un valor de contexto, todos los componentes que consumen ese contexto se volverán a representar, incluso si están envueltos en React.memo(). (Si no sabe qué React.memo()es, no se asuste: lo analizaremos pronto). Esto puede empeorar el rendimiento de una aplicación.

Pero podemos mitigar este problema con los siguientes métodos:

1. Utilice múltiples contextos de React

Esto es lo que discutimos anteriormente y es la forma “preferida” de resolver el problema de rerenderización ( ver esta respuesta ).

2. Divida el componente y pase el valor necesario

También puedes dividir el componente y pasar hacia abajo (como una propiedad) el valor necesario del contexto, con los componentes secundarios envueltos en React.memo(). Ejemplo:

const Card = () => {
  const appContextValue = useContext(AppContext);
  const theme = appContextValue.theme;

  return (
    <div>
      <CardTitle theme={theme} />
      <CardDescription theme={theme} />
    </div>
  );
};

const CardTitle = React.memo(({ theme }) => {
  return <h2 style={{ color: theme.text }}>This is the Title </h2>;
});

const CardDescription = React.memo(({ theme }) => {
  return <p style={{ color: theme.text }}>lorem ipsum dolor sit amet,</p>;
});

React.memo()es un componente de orden superior (HOC) en React que se utiliza para optimizar los componentes funcionales al evitar repeticiones de renderización innecesarias. Esto se logra memorizando el componente, lo que significa que solo se volverá a renderizar si cambian sus propiedades.

  • Sin React.memo(): los componentes CardTitleCardDescriptionse volverían a renderizar cada vez que su componente principal, Card, se vuelva a renderizar, incluso si sus propiedades no han cambiado. Esto puede generar problemas de rendimiento en aplicaciones más grandes o con componentes que son costosos de renderizar.
  • Con React.memo()CardTitleCardDescriptionsolo volver a renderizar si sus propiedades cambian, lo que reduce los renderizados innecesarios y mejora el rendimiento.

Entonces, al dividir el componente, pasar solo los valores que se necesitan como propiedades y envolver los componentes en React.memo()CardTitleCardDescriptionsolo se volverá a renderizar si themese actualiza, pero no si usernamese actualiza.

Esta solución es particularmente útil si no podemos dividir el contexto por cualquier motivo.

3. Un componente con React.useMemo()interior

A continuación, themese muestra una dependencia de useMemo(), por lo que solo obtendremos una nueva representación de los elementos devueltos por la función de devolución de llamada cuando themese cambia:

const Card = () => {
  const appContextValue = useContext(AppContext);
  const theme = appContextValue.theme;

  return useMemo(
    () => (
      <div>
        <CardTitle theme={theme} />
        <CardDescription theme={theme} />
      </div>
    ),
    [theme]
  );
};

const CardTitle = ({ theme }) => {
  return <h2 style={{ color: theme.text }}>This is the Title </h2>;
};

const CardDescription = ({ theme }) => {
  return <p style={{ color: theme.text }}>lorem ipsum dolor sit amet,</p>;
};

Así es como useMemo()funciona:

  1. El primer parámetro de useMemo()es una función de devolución de llamada que devuelve un valor memorizado. En este caso, devuelve un elemento React o un árbol de elementos React.
  2. El segundo parámetro es una matriz de dependencias. Si se actualiza alguno de los valores de esta matriz de dependencias, se llama a la función de devolución de llamada proporcionada como primer argumento y se vuelven a representar los elementos que devuelve la función de devolución de llamada.

Por lo tanto, useMemo()se puede usar para volver a renderizar elementos React solo si se actualizan ciertos valores especificados en la matriz de dependencia.

Al envolver estos elementos en useMemo()y especificarlos themecomo la única dependencia, los elementos solo se volverán a representar si themese actualiza, pero no se volverán a representar si se actualiza cualquier otro valor de contexto.

Esta solución también es especialmente útil si no podemos dividir el contexto.

API de contexto de React frente a Redux

Este es un tema muy común y muy debatido dentro de la comunidad de React. React Context API y Redux son herramientas para gestionar el estado en una aplicación React, pero tienen diferentes casos de uso, fortalezas y limitaciones.

La API de contexto es una característica incorporada de React, cuyo objetivo principal es permitir que el estado se comparta en un árbol de componentes de React sin necesidad de realizar perforaciones en la estructura.

La API de contexto tiene una API simple: React.createContext()Providery el useContext()gancho. Y es buena para aplicaciones pequeñas y medianas, ya que es fácil de usar y requiere poca configuración y código repetitivo.

Por otro lado, Redux es una biblioteca de gestión de estados que debe instalarse como un paquete de terceros en una aplicación. Su propósito principal es gestionar el estado de toda la aplicación de una manera predecible, especialmente en aplicaciones grandes y complejas.

Por qué Context API es bueno para aplicaciones pequeñas y medianas:

  • Simplicidad : es más simple que Redux.
  • Integrado : es parte de React, por lo que no es necesario instalar paquetes adicionales, lo que facilita el mantenimiento del proyecto.
  • Código repetitivo mínimo : requiere menos código repetitivo y configuración que Redux.

Por qué Redux es bueno para aplicaciones más grandes y complejas:

  • Tienda única : mantiene una tienda única para todo el estado de la aplicación, lo que facilita la depuración y las pruebas.
  • Actualizaciones de estado predecibles : utiliza funciones puras (reductores) para administrar las actualizaciones de estado, lo que garantiza la previsibilidad y la inmutabilidad.
  • Soporte de middleware : Potente sistema de middleware (como redux-thunk o redux-saga) para manejar acciones asincrónicas y efectos secundarios.
  • Integración de DevTools : excelentes herramientas de desarrollo para la depuración de viajes en el tiempo y la inspección de estado.
  • Adecuado para aplicaciones grandes : diseñado para manejar lógica de estados compleja y aplicaciones a gran escala.

El mantenedor de Redux, Mark Erikson , da las siguientes razones para usar Redux :

  • Patrones arquitectónicos consistentes
  • Capacidades de depuración
  • Software intermedio
  • Complementos y extensibilidad
  • Uso multiplataforma y multiframework
  • Dependiendo de la configuración de su aplicación, el rendimiento es mucho mejor que trabajar solo con Context (no tenemos que preocuparnos por el problema de re-renderización que tenemos con Context, mencionado anteriormente; los componentes solo se re-renderizan cuando se actualiza el valor que están usando)

En resumen:

  • Redux es una herramienta de gestión de estados más compleja que ofrece más funciones y herramientas. Proporciona una forma coherente de gestionar el estado en toda una aplicación, lo que resulta muy útil en proyectos más grandes con varios desarrolladores (ya que no todos implementarán sus propios estilos de gestión de estados y harán que la base de código sea incoherente).
  • La API de contexto de React es más sencilla, requiere menos configuración y es una buena solución para proyectos pequeños a medianos donde no es necesaria la complejidad y la sobrecarga adicionales que implica usar una herramienta como Redux.

¡Gracias por leer!

Si este artículo te resultó útil, puedes escuchar más de mí:

Curso gratuito sobre Hooks para React

¿Quieres aprender todos los hooks de React? He creado un vídeo gratuito de 2 horas en el que se explican los 9 hooks principales de React con ejemplos: Tutorial de hooks de React: todos los hooks de React explicados con ejemplos . Si te gusta, suscríbete a mi canal .

¡Salud!

Shopping cart0
Aún no agregaste productos.
Seguir viendo
0