Cómo usar la Context API en React

React

En este tutorial vamos a ver cómo utilizar la Context API en React, tanto en componentes de clase como en componentes funcionales mediante hooks. Además, crearemos un proyecto de ejemplo para demostrar su uso.

Antes de continuar con este tutorial, es recomendable que hayas usado React previamente o que hayas seguido el tutorial de introducción a React. Además, también deberías saber utilizar las hooks de React, que son un modo de gestionar estados en componentes funcionales. Si nunca has usado las hooks, es recomendable que consultes el tutorial en el que explico cómo crear una aplicación con React usando hooks.

Qué es la Context API de React

La Context API de React es una forma de crear variables globales que podrás compartir fácilmente con otros componentes de tu aplicación. La alternativa consiste en pasar las propiedades de un componente padre a un componente hijo o nieto, y así sucesivamente en este orden descendente.

También podrías usar una librería de gestión de estado como Redux. Sin embargo, el uso de contextos mediante la Context API es una solución más ligera y sencilla que no está mal para aplicaciones que no son excesivamente grandes.

Cómo crear un contexto

En ocasiones puede ser deseable que ciertos datos se compartan en toda la aplicación, de modo que puedas usarlos en todos los componentes React. Por ejemplo, este es el caso de ciertas variables que definen los etilos generales de los componentes, que podrían cambiar en base al tema seleccionado o a varias opciones de visualización. Otro ejemplo es el modo noche, que quizás cambie ciertos elementos de los componentes en base a algún parámetro. También podrías obtener ciertos datos de una API que quieres compartir entre diferentes componentes. Para lograr estas cosas puedes usar un contexto.

En el siguiente ejemplo vamos a crear un contexto al que llamaremos ContextoUsuario, que dará lugar a los componentes ContextoUsuario.Provider y ContextoUsuario.Consumer. Un Provider no es otra que el componente que proporciona el valor, mientras un Consumer es el componente que utiliza o consume el valor.

Para crear el contexto vamos a usar el método React.createContext(), que usaremos en un archivo de ejemplo al que llamaremos ContextoUsuario.js:

import React from 'react';

const ContextoUsuario = React.createContext();

export const ProviderUsuario = ContextoUsuario.Provider;
export const ConsumerUsuario = ContextoUsuario.Consumer;

export default ContextoUsuario;

El método React.createContext() acepta un objeto opcional como valor, pero en este caso lo representamos como vacío, ya que siempre podremos establecer su valor más tarde, caso que se daría de obtener los datos desde una API. Sin embargo, podemos establecer cualquier valor inicial, como por ejemplo React.createContext(true).

Proporciona un contexto con un Provider

El Provider de un contexto siempre debe envolver a modo de wrapper a los Consumers del contexto. En el siguiente ejemplo envolvemos todo el componente App de la aplicación en el Provider que hemos creado. Creamos un objeto llamado usuario y se lo pasamos a los componentes que descienden del Provider ProviderUsuario como un valor de una propiedad del mismo:

import React from 'react';
import PaginaInicio from './PaginaInicio';
import { ProviderUsuario } from './ContextoUsuario';

function App() {
  const usuario = { nombre: 'Edu', modoOscuro: true };

  return (
    <ProviderUsuario value={usuario}>
      <PaginaInicio />
    </ProviderUsuario>
  );
}

Ahora cualquier componente que descienda del componente App podrá acceder al valor usuario como una propiedad. Sin embargo, no podrás obtenerlo accediendo a this.props o a this.state en los componentes que desciendan de App, sino que tendrás que hacerlo de uno de los modos que veremos a continuación en función del tipo de componente que utilices.

Consume el contexto mediante Consumers

Una vez proporcionado el contexto a la aplicación mediante un Provider, podemos consumirlo de varias formas en función del tipo de componente, que podrá ser un componente de clase o un componente funcional. Vamos a ver ambos casos.

Componentes de clase

En los componentes de clase puedes consumir el contexto de varias formas. El modo más habitual consiste en acceder al elemento estático contextType. Por ejemplo, vamos a obtener el contexto en el componente PaginaInicio que hemos incluido en nuestro componente App:

import React, { Component } from 'react';
import ContextoUsuario from './ContextoUsuario';

class PaginaInicio extends Component {
  static contextType = ContextoUsuario;
}

Ahora, en caso de querer acceder al contexto en un método del componente, accederíamos a this.context:

componentDidMount() {
  const usuario = this.context;
  console.log(usuario); // { nombre: 'Edu', modoOscuro: true };
}

Una vez asignado el contexto a la constante usuario, ya podremos mostrar los datos del usuario en el método render:

render() {
  return <div>{usuario.nombre}</div>
}

Tal y como ves, mediante el método anterior no hemos tenido que importar el Consumer UsuarioConsumer. Sin embargo, también podremos acceder a un contexto importando un Consumer e incluyendo el código en el que queremos usarlo en su interior. De este modo, podremos obtener a los valores del contexto accediendo a las propieades o props del componente. En el caso de nuestro ejemplo, primero importamos el Consumer UsuarioConsumer y luego lo usamos en el método render para acceder a los datos del usuario:

import React, { Component } from 'react';
import { ConsumerUsuario } from './ContextoUsuario';

class PaginaInicio extends Component {
  render() {
    return (
      <ConsumerUsuario>
        {(props) => {
          return <div>{props.nombre}</div>
        }}
      </ConsumerUsuario>
    )
  }
}

Componentes funcionales

Para acceder a un contexto en un componente funcional, tendrás que importar el contexto en el archivo en el que declaras el componente. Luego debes usar la función useContext, que es equivalente al uso de static contextType en los componentes de clase.

En este ejemplo, importamos el contexto ContextoUsuario y lo aplicamos al componente mediante la función useContext:

import React, { useContext } from 'react';
import ContextoUsuario from './ContextoUsuario';

export const PaginaInicio = () => {
  const usuario = useContext(ContextoUsuario);

  return <div>{usuario.nombre}</div>;
}

Cómo actualizar el contexto

Actualizar el contexto es una tarea muy sencilla que no difiere mucho del modo en el que actualizarías el estado de un componente. Lo más recomendable es crear una clase que contenga el estado de un contexto junto con los métodos necesarios para actualizarlo. Podemos hacerlo en el propio archivo en el que hemos definido el contexto ContextoUsuario:

import React, { Component } from 'react';

const ContextoUsuario = React.createContext();

class ProviderUsuario extends Component {

  state = {
    usuario: {},
  }

  setUsuario = (usuario) => {
    this.setState((prevState) => ({ usuario }));
  }

  render() {
    const { descendientes } = this.props;
    const { usuario } = this.state;
    const { setUsuario } = this;

    return (
      <ContextoUsuario.Provider
        value={{
          usuario,
          setUsuario,
        }}
      >
        { descendientes }
      </ContextoUsuario.Provider>
    )
  }
}

export default ContextoUsuario;

export { ProviderUsuario };

Ahora ya podrás ver y actualizar los datos del usuario desde el Contexto:

import React, { Component } from 'react';
import ContextoUsuario from './ContextoUsuario';

class PaginaInicio extends Component {
  
  static contextType = ContextoUsuario;

  render() {
    const { usuario, setUsuario } = this.context;

    return (
      <div>
        <button
          onClick={() => {
            const datosUsuario = { nombre: 'Eduardo', modoOscuro: false }
            setUsuario(datosUsuario);
          }}
        >
          Actualizar usuario
        </button>
        <p>{`Nombre del usuario: ${usuario.nombre}`}</p>
        <p>{`Modo noche: ${usuario.modoOscuro}`}</p>
      </div>
    )
  }
}

Guía rápida de la Context API

A continuación puedes ver los métodos y funciones que te permiten crear un contexto, acceder a él y actualizarlo:

  • Para crear un contexto, usa la siguiente sentencia, reemplazando Contexto por el nombre del contexto y luego extrae el Provider y el Consumer del contexto:
    const Contexto = React.createContext();
    const ProviderContexto = Contexto.Provider;
    const ConsumerContexto = Contexto.Consumer;
  • A la hora de usar el contexto, incluye el componente en el que quieres usarlo en el interior del Provider del contexto:
    <ProviderContexto value={ elemento }>
      <NombreComponente/>
    </ProviderContexto>
  • Para consumir el contexto en un componente de clase , asigna contextType al contexto:
    static contextType = Contexto;
  • Para consumir el contexto en un componente funcional, usa la función useContext pasándole el contexto como parámetro:
    const elemento = useContext(Contexto);

Y esta pequeña chuleta es todo lo que habitualmente necesitarás en caso de usar la Context API.

Problemas de la Context API

El uso de la Context API es aceptable para aplicaciones pequeñas, pero sus ventajas se difuminan cuando es usada en aplicaciones de tamaño medio o grande. El mayor problema es la imposibilidad de usar múltiples elementos contextType estáticos en un componente. Por ello, te verás obligado a crear un único contexto global de gran tamaño. Además, el método mediante el cual usamos el Provider de un contexto a modo de wrapper hace que los componentes sean difíciles de testear.

Esta limitación de la Context API hace que tengamos que buscar otras alternativas cuando las aplicaciones crecen.  Una de las mejores alternativas consiste en usar una librería de gestión de estados como Redux.


Avatar de Edu Lazaro

Edu Lázaro: Ingeniero técnico en informática, actualmente trabajo como desarrollador web y programador de videojuegos.

👋 Hola! Soy Edu, me encanta crear cosas y he redactado esta guía. Si te ha resultado útil, el mayor favor que me podrías hacer es el de compatirla en Twitter 😊

Si quieres conocer mis proyectos, sígueme en Twitter.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

“- Hey, Doc. No tenemos suficiente carretera para ir a 140/h km. - ¿Carretera? A donde vamos, no necesitaremos carreteras.”