Funcionamiento de React Context

React Context es una funcionalidad de React que nos permite evitar la redundancia de pasar props entre componentes (A lo que se conoce como "props drilling"). En lugar de eso, React Context nos permite pasar valores entre componentes usando una sintaxis especial que nos ahorra el tener que pasar los props muy profundamente en el árbol de componentes.

Es importante tener en cuenta que NO se debe usar React Context en todos los casos. Esta funcionalidad NO reemplaza el patrón normal de pasar props entre componentes ni tampoco reemplaza herramientas o librerías como Redux.

Usualmente react context se utiliza para compartir datos que se consideran "globales", como el theme de la aplicación, algunas propiedades del idioma de la aplicación o quizás los datos del usuario que inició sesión. Debemos tener cuidado al usarlo y pensar bien en el caso de uso para no causar problemas en el “árbol” de componentes de la aplicación.

Ahora, para iniciar un “contexto”, habra que importar la función de “createContext” y definirselo a una nueva variable. Imáginemos que un usuario inició sesión y tenemos un objeto con su información, el cual queremos mostrarlo en diferentes lugares de nuestra aplicación.

import React, { createContext } from 'react'

const user = {
  firstName: 'Cristiano',
  lastName: 'Ronaldo',
  nickname: 'El_Bicho',
}

const UserContext = createContext()

Ahora que ya tenemos definido nuestro contexto, podemos utilizarlo como si fuera un componente normal al que le queremos pasar algún "children". Entonces tendremos que “wrappear”/envolver los componentes en donde utilizaremos los datos de nuestro usuario. El componente se vería algo parecido a esto:

import Dashboard from 'components/Dashboard'

export default function App() {
  return (
    <UserContext>
      <div className="flex flex-col">
        {/* insert Menu here */}
        <Dashboard />
      </div>
    </UserContext>
  )
}

Mucho ojo aquí, ya que es donde se revuelven algunas cosas.

Ya que tenemos listo nuestro componente, podemos pasar nuestro objeto user usando el atributo value de la propiedad “Provider” de UserContext (Que es nuestro contexto). El provider va a proveer nuestra data a nuestros componentes “Hijos” (Los que están dentro del contexto UserContext) y va a permitir que los componentes que lo consuman se suscriban a los cambios del contexto. Esto quiere decir que si nuestro objeto usuario cambia, los componentes consumidores de nuestro provider, se van a volver a renderizar y tomaran los nuevos datos. (Para simplicidad del ejemplo, nuestro user es estático y nunca va a cambiar). Cambiemos un poco nuestro codigo para crear nuestro provider.

import React, { createContext } from 'react'
import Dashboard from 'components/Dashboard'

const user = {
  firstName: 'Cristiano',
  lastName: 'Ronaldo',
  nickname: 'El_Bicho',
}

const UserContext = createContext()

export default function App() {
  return (
    <UserContext.Provider value={user}>
      <div className="flex flex-col">
        {/* insert Menu here */}
        <Dashboard />
      </div>
    </UserContext.Provider>
  )
}

Una vez teniendo listo nuestro provider, ya podemos iniciar nuestro consumidor. Para esto, nuestro contexto viene con un método llamado Consumer que podemos utilizar de la siguiente manera. export const UserConsumer = UserContext.Consumer

import React, { createContext } from 'react'
import Dashboard from 'components/Dashboard'

const user = {
  firstName: 'Cristiano',
  lastName: 'Ronaldo',
  nickname: 'El_Bicho',
}

const UserContext = createContext()
export const UserConsumer = UserContext.Consumer

export default function App() {
  return (
    <UserContext.Provider value={user}>
      <div className="flex flex-col">
        {/* insert Menu here */}
        <Dashboard />
      </div>
    </UserContext.Provider>
  )
}

Exportando nuestro consumidor, lo podemos utilizar en cualquier componente que este por debajo de nuestro provider en el “árbol” de componente.

Imaginemos que dentro de nuestro componente de Dashboard, utilizamos otro componente que es una gráfica de resultados, en donde queremos mostrar el nickname de nuestro usuario.

Para eso hariamos algo asi:

import { UserConsumer } from 'App';

export default function UserChart() {

  return (
	<UserConsumer value={user}>
		{(user) => (
			<div className='flex flex-col'>
				<h1>{user.nickname}</h1>
		  	< -- Lógica de nuestro componente ->
		  </div>
		)}
	</UserConsumer>
  );
}

Como puedes observar, nuestro UserConsumer nos permite utilizar una funcion con un argumento que va a ser nuestro value del provider, en este caso nuestro usuario. De esta manera podemos utilizar el objeto, sin tener que pasarle props a nuestro componente UserChart.

Después de entender como funciona el consumer y provider de un contexto, podemos entender mejor un hook muy importante que hará nuestras vidas mas sencillas. En lugar de crear y exportar el consumer de nuestro contexto, vamos a exportar el contexto completo.

import React, { createContext } from 'react'
import Dashboard from 'components/Dashboard'

const user = {
  firstName: 'Cristiano',
  lastName: 'Ronaldo',
  nickname: 'El_Bicho',
}

export const UserContext = createContext()

export default function App() {
  return (
    <UserContext.Provider value={user}>
      <div className="flex flex-col">
        {/* insert Menu here */}
        <Dashboard />
      </div>
    </UserContext.Provider>
  )
}
- export const UserConsumer = UserContext.Consumer
+ const UserContext = createContext()

Con esto podremos importar nuestro contexto en nuestro componente de <UserChart /> (Que está dentro del componente <Dashboard />) y usar el hook useContext de la siguiente manera.

import { useContext } from 'react';
import { UserContext } from 'App';

export default function UserChart() {
  const user = useContext(UserContext);

  return (
    <div className='flex flex-col'>
      <h1>{user.nickname}</h1>
      < -- Lógica de nuestro componente ->
    </div>
  );
}

Y listo! Ya podemos utilizar nuestro UserContext dentro de cualquier hijo del Provider. 💃

Espero que esto haya sido de utilidad! En otros posts estaré platicando algunas otros "trucos" con react context.