El proveedor de contexto en React es una herramienta que permite compartir información entre componentes de manera fácil y sencilla. Con los proveedores de contexto, los componentes pueden acceder a datos y funciones desde un contexto compartido sin tener que pasar props manualmente a través de varios niveles de componentes. En este tutorial, aprenderás cómo usar los proveedores de contexto en React para compartir datos entre componentes sin problemas.

En el desarrollo de aplicaciones con React, a menudo nos encontramos con la necesidad de compartir datos entre componentes que no están directamente relacionados. Este es un problema común que puede ser resuelto utilizando los proveedores de contexto de React.

Los puntos importantes a considerar son:

  • ¿Cómo se crea un contexto?
  • ¿Cómo se comparten los datos a través del contexto?
  • ¿Cómo se consumen los datos del contexto en los componentes?

Vamos a resolver este problema creando una aplicación simple que mantiene una lista de pacientes y permite agregar nuevos pacientes a la lista.

Creación del Contexto

Primero, creamos un contexto llamado AppContext en el archivo AppContext.js. Este contexto inicialmente tiene una lista vacía de pacientes y dos funciones, setListaPacientes y getPaciente, que lanzan un error si no se implementan.

Importamos React: Primero, importamos la biblioteca React para poder utilizar sus funcionalidades.

import React from "react";

Creamos el Contexto: Luego, creamos un contexto utilizando React.createContext(). Este contexto nos permitirá compartir datos entre componentes sin necesidad de pasar props de forma manual a cada nivel.

import React from "react";

export const AppContext = React.createContext({});

Definimos el estado inicial del Contexto: Dentro de React.createContext(), definimos el estado inicial de nuestro contexto. En este caso, tenemos un array vacío llamado listaPacientes y dos funciones, setListaPacientes y getPaciente, que lanzarán un error si se intentan utilizar sin haber sido implementadas.

import React from "react";

export const AppContext = React.createContext({
  listaPacientes: [],
  setListaPacientes: () => {
    throw new Error("No implementado");
  },
  getPaciente: () => {
    throw new Error("No implementado");
  },
});

Proveedor de Contexto

Luego, en el archivo MainComponent.js, creamos un componente que actúa como el proveedor de contexto. Este componente mantiene el estado de la lista de pacientes y proporciona las implementaciones para setListaPacientes y getPaciente.

Importamos los Componentes Necesarios: Primero, importamos React y Component de la biblioteca React. También importamos el contexto que hemos creado en “AppContext.js” y los componentes “TableComponent” y “FormComponent” que vamos a utilizar.

import React, { Component } from "react";
import { AppContext } from "../common/AppContext";
import TableComponent from "./TableComponent";
import FormComponent from "./FormComponent";

Creamos el Componente Principal: Creamos un componente de clase llamado “MainComponent”. Este componente va a ser el encargado de manejar el estado de nuestra aplicación y de proveer el contexto a los componentes hijos.

import React, { Component } from "react";
import { AppContext } from "../common/AppContext";
import TableComponent from "./TableComponent";
import FormComponent from "./FormComponent";

export default class MainComponent extends Component {
  constructor(props) {
    super(props);
    this.state = {};
  }

  render = () => <React.Fragment></React.Fragment>;
}

Definimos el Estado Inicial: En el constructor del componente, definimos el estado inicial de nuestra aplicación. Tenemos una lista de pacientes y dos funciones, setListaPacientes y getPaciente, que hemos implementado.

import React, { Component } from "react";
import { AppContext } from "../common/AppContext";
import TableComponent from "./TableComponent";
import FormComponent from "./FormComponent";

export default class MainComponent extends Component {
  constructor(props) {
    super(props);
    this.state = {
      listaPacientes: [
        { nombre: "Paloma" },
        { nombre: "Carmen" },
        { nombre: "Damian" },
      ],
      setListaPacientes: this.setListaPacientes,
      getPaciente: this.getPaciente,
    };
  }

  render = () => <React.Fragment></React.Fragment>;
}

Implementamos las Funciones del Contexto: Implementamos las funciones setListaPacientes, que añade un nuevo paciente a la lista, y getPaciente, que busca un paciente en la lista por su nombre.

import React, { Component } from "react";
import { AppContext } from "../common/AppContext";
import TableComponent from "./TableComponent";
import FormComponent from "./FormComponent";

export default class MainComponent extends Component {
  constructor(props) {
    super(props);
    this.state = {
      listaPacientes: [
        { nombre: "Paloma" },
        { nombre: "Carmen" },
        { nombre: "Damian" },
      ],
      setListaPacientes: this.setListaPacientes,
      getPaciente: this.getPaciente,
    };
  }

  setListaPacientes = (paciente) =>
    this.setState({ listaPacientes: [...this.state.listaPacientes, paciente] });

  getPaciente = (nombre) =>
    this.state.listaPacientes.find((paciente) => paciente.nombre === nombre);

  render = () => <React.Fragment></React.Fragment>;
}

Renderizamos los Componentes Hijos: En la función render, utilizamos el componente AppContext.Provider para proveer el estado de nuestra aplicación a los componentes hijos. Dentro del proveedor, renderizamos los componentes “TableComponent” y “FormComponent”.

import React, { Component } from "react";
import { AppContext } from "../common/AppContext";
import TableComponent from "./TableComponent";
import FormComponent from "./FormComponent";

export default class MainComponent extends Component {
  constructor(props) {
    super(props);
    this.state = {
      listaPacientes: [
        { nombre: "Paloma" },
        { nombre: "Carmen" },
        { nombre: "Damian" },
      ],
      setListaPacientes: this.setListaPacientes,
      getPaciente: this.getPaciente,
    };
  }

  setListaPacientes = (paciente) =>
    this.setState({ listaPacientes: [...this.state.listaPacientes, paciente] });

  getPaciente = (nombre) =>
    this.state.listaPacientes.find((paciente) => paciente.nombre === nombre);

  render = () => (
    <React.Fragment>
      <AppContext.Provider value={this.state}>
        <div className="container-fluid m-2">
          <div className="row">
            <div className="col col-md-6">
              <TableComponent />
            </div>
            <div className="col col-md-6">
              <FormComponent />
            </div>
          </div>
        </div>
      </AppContext.Provider>
    </React.Fragment>
  );
}

Consumidores de Contexto

Finalmente, creamos dos componentes, TableComponent y FormComponent, que consumen los datos del contexto.

TableComponent muestra la lista de pacientes en una tabla:

Importamos los Componentes Necesarios: Primero, importamos React y Component de la biblioteca React. También importamos el contexto que hemos creado en “AppContext.js”.

import React, { Component } from "react";
import { AppContext } from "../common/AppContext";

Creamos el Componente de la Tabla: Creamos un componente de clase llamado “TableComponent”. Este componente va a ser el encargado de mostrar la lista de pacientes en una tabla.

import React, { Component } from "react";
import { AppContext } from "../common/AppContext";

export default class TableComponent extends Component {
  constructor(props) {
    super(props);
    this.state = {};
  }

  render = () => <React.Fragment></React.Fragment>;
}

Definimos el Contexto: Utilizamos static contextType = AppContext; para definir el contexto de nuestro componente. Esto nos permitirá acceder al estado compartido de nuestra aplicación a través de this.context.

import React, { Component } from "react";
import { AppContext } from "../common/AppContext";

export default class TableComponent extends Component {
  constructor(props) {
    super(props);
    this.state = {};
  }

  static contextType = AppContext;

  render = () => <React.Fragment></React.Fragment>;
}

Renderizamos la Tabla: En la función render, utilizamos this.context.listaPacientes.map() para iterar sobre la lista de pacientes y mostrar cada uno en una fila de la tabla.

import React, { Component } from "react";
import { AppContext } from "../common/AppContext";

export default class TableComponent extends Component {
  constructor(props) {
    super(props);
    this.state = {};
  }

  static contextType = AppContext;

  render = () => (
    <React.Fragment>
      <table className="table table-striped table-bordered">
        <thead>
          <tr className="row">
            <th className="col col-md-6">ID</th>
            <th className="col col-md-6">Nombre</th>
          </tr>
        </thead>
        <tbody>
          {this.context.listaPacientes.map((paciente, index) => (
            <tr className="row">
              <td className="col col-md-6">{index + 1}</td>
              <td className="col col-md-6">{paciente.nombre}</td>
            </tr>
          ))}
        </tbody>
      </table>
    </React.Fragment>
  );
}

TableComponent

FormComponent permite agregar nuevos pacientes a la lista:

Importamos los Componentes Necesarios: Primero, importamos React y Component de la biblioteca React. También importamos el contexto que hemos creado en “AppContext.js”.

import React, { Component } from "react";
import { AppContext } from "../common/AppContext";

Creamos el Componente del Formulario: Creamos un componente de clase llamado “FormComponent”. Este componente va a ser el encargado de recoger la información del nuevo paciente a través de un formulario.

import React, { Component } from "react";
import { AppContext } from "../common/AppContext";

export default class FormComponent extends Component {
  constructor(props) {
    super(props);
    this.state = {};
  }

  render = () => <React.Fragment></React.Fragment>;
}

Definimos el Estado Inicial: En el constructor del componente, definimos el estado inicial de nuestro componente. Tenemos un objeto newPaciente con una propiedad nombre que está inicialmente vacía.

import React, { Component } from "react";
import { AppContext } from "../common/AppContext";

export default class FormComponent extends Component {
  constructor(props) {
    super(props);
    this.state = {
      newPaciente: { nombre: "" },
    };
  }

  render = () => <React.Fragment></React.Fragment>;
}

Definimos el Contexto: Utilizamos static contextType = AppContext; para definir el contexto de nuestro componente. Esto nos permitirá acceder al estado compartido de nuestra aplicación a través de this.context.

import React, { Component } from "react";
import { AppContext } from "../common/AppContext";

export default class FormComponent extends Component {
  constructor(props) {
    super(props);
    this.state = {
      newPaciente: { nombre: "" },
    };
  }

  static contextType = AppContext;

  render = () => <React.Fragment></React.Fragment>;
}

Implementamos las Funciones del Componente: Implementamos las funciones updateTextValue, que actualiza el valor del nuevo paciente a medida que el usuario escribe en el formulario, y submitForm, que añade el nuevo paciente a la lista si no existe ya un paciente con el mismo nombre.

import React, { Component } from "react";
import { AppContext } from "../common/AppContext";

export default class FormComponent extends Component {
  constructor(props) {
    super(props);
    this.state = {
      newPaciente: { nombre: "" },
    };
  }

  static contextType = AppContext;

  updateTextValue = (event) =>
    this.setState({ newPaciente: { nombre: event.target.value } });

  submitForm = () => {
    if (!this.context.getPaciente(this.state.newPaciente.nombre)) {
      this.context.setListaPacientes(this.state.newPaciente);
    }
    this.setState({ newPaciente: { nombre: "" } });
  };

  render = () => <React.Fragment></React.Fragment>;
}

Renderizamos el Formulario: En la función render, renderizamos un formulario con un campo de texto para el nombre del paciente y un botón para enviar el formulario. Utilizamos this.state.newPaciente.nombre para el valor del campo de texto y this.updateTextValue y this.submitForm para los eventos de entrada y clic del botón, respectivamente.

import React, { Component } from "react";
import { AppContext } from "../common/AppContext";

export default class FormComponent extends Component {
  constructor(props) {
    super(props);
    this.state = {
      newPaciente: { nombre: "" },
    };
  }

  static contextType = AppContext;

  updateTextValue = (event) =>
    this.setState({ newPaciente: { nombre: event.target.value } });

  submitForm = () => {
    if (!this.context.getPaciente(this.state.newPaciente.nombre)) {
      this.context.setListaPacientes(this.state.newPaciente);
    }
    this.setState({ newPaciente: { nombre: "" } });
  };

  render = () => (
    <React.Fragment>
      <div className="container-fluid">
        <div className="form-group">
          <label className="form-label">Nombre:</label>
          <input
            type="text"
            className="form-control"
            value={this.state.newPaciente.nombre}
            onInput={this.updateTextValue}
          />
        </div>
        <button
          className="btn btn-primary btn-md mt-2"
          onClick={this.submitForm}
        >
          Enviar
        </button>
      </div>
    </React.Fragment>
  );
}

FormComponent

Finalización

Durante el desarrollo de esta aplicación, uno de los principales desafíos fue asegurarse de que los datos se compartieran correctamente entre los componentes. Es importante recordar que el contexto debe ser proporcionado en un componente que sea ancestro común de todos los componentes que necesitan acceder a los datos.

Recomendaría usar los proveedores de contexto de React para compartir datos entre componentes cuando los props no son una opción viable. Sin embargo, es importante tener en cuenta que el uso excesivo del contexto puede hacer que la aplicación sea difícil de entender y mantener.

Una pregunta interesante para el debate podría ser: ¿Cuándo es apropiado usar el contexto en lugar de Redux? ¿Existen casos en los que uno es claramente superior al otro? ¡Espero tus comentarios!

Leave a Comment

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

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.