No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

Aprende todo un fin de semana sin pagar una suscripción 🔥

Aprende todo un fin de semana sin pagar una suscripción 🔥

Regístrate

Comienza en:

3D
20H
21M
9S
Curso de Introducción a React.js

Curso de Introducción a React.js

Juan David Castro Gallego

Juan David Castro Gallego

useContext

17/23
Recursos

El hook de contexto nos ayuda a acceder a datos globales de nuestro contexto, desde cualquier componente hijo, sin tener que pasar estos datos por props componente por componente.

Tiene la misma funcionalidad que el consumer de nuestro contexto, pero useContext también tiene una manera más sencilla de utilizar y una sintaxis mucho más clara.

Context.Consumer a useContext

import React from 'react';
// También es importante importar nuestro contexto
import { TodoContext } from '../TodoContext';
import { TodoCounter } from '../TodoCounter';
import { TodoSearch } from '../TodoSearch';
import { TodoList } from '../TodoList';
import { TodoItem } from '../TodoItem';
import { CreateTodoButton } from '../CreateTodoButton';

function AppUI() {
  // Desesctructuramos los valores de nuestro contexto
  const {
    error,
    loading,
    searchedTodos,
    completeTodo,
    deleteTodo,
  } = React.useContext(TodoContext);
  
  return (
    
      
      

      
        {error && 

Desespérate, hubo un error...

} {loading &&

Estamos cargando, no desesperes...

} {(!loading && !searchedTodos.length) &&

¡Crea tu primer TODO!

} {searchedTodos.map(todo => ( completeTodo(todo.text)} onDelete={() => deleteTodo(todo.text)} /> ))}
); } export { AppUI };

Ahora solo nos queda utilizar este hook para acceder a nuestro contexto desde los componentes faltantes.

TodoCounter.js

import React from 'react';
import { TodoContext } from '../TodoContext';
import './TodoCounter.css';

function TodoCounter() {
  const { totalTodos, completedTodos } = React.useContext(TodoContext);
  
  return (
    <h2 className="TodoCounter">Has completado {completedTodos} de {totalTodos} TODOsh2>
  );
}

export { TodoCounter };

TodoSearch.js

import React from 'react';
import { TodoContext } from '../TodoContext';
import './TodoSearch.css';

function TodoSearch() {
  const { searchValue, setSearchValue } = React.useContext(TodoContext);
  
  const onSearchValueChange = (event) => {
    console.log(event.target.value);
    setSearchValue(event.target.value);
  };

  return (
    <input
      className="TodoSearch"
      placeholder="Cebolla"
      value={searchValue}
      onChange={onSearchValueChange}
    />
  );
}

export { TodoSearch };

¿Cuándo se recomienda emplear React Context?

  • Estado global
  • Tema
  • Configuración de la app
  • Autenticación de usuario
  • Configuración de usuario
  • Lenguaje preferido
  • Colección de servicios

Contribución creada por: Brandon Argel.

Aportes 40

Preguntas 17

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

o inicia sesión.

Basicamente, la formula de useContext es

import { Contexto } from './direccionContexto/'

const { value } = React.useContext(Contexto)

Me encanto este video, me venia haciendo mucho ruido la forma de pasar props padre>hijo>nieto>...>x todo el tiempo.
.
Creo que esta técnica es excelente. ¿Hay alguna forma de como investigar sobre estos patrones por asi decirlo, para buscar buenas prácticas de como escribir nuestros componentes en React?
.
Si ya desde el principio comienzo a escribir con buenas prácticas creo que va a ser mejor y hará más claro mi código.

En React, cuándo debo iniciar con mayuscula o minuscula el nombre del archivo como por ejemplo TodoCounter.js o useLocalStorage.js?

Si vienes de Vue, puedes utilizar una herramienta muy similar y mas facil de manejar llamada provide/inject. Buscalo en la documentacion de vue.

provide/inject

No se olviden que el argumento de useContext debe ser nuestro objeto contexto y no por separado el provider o el consumer, como el profe lo habia planteado que podria usarse cuando lo haciamos con la otra técnica sin el hook useContext
.
Correcto: useContext(MiContexto)
Incorrecto: useContext(MiContexto.Consumer)
Incorrecto: useContext(MiContexto.Provider)

Por si a alguien le habia ocurrido un error con el useContext.

Si al usar useContext el componente da error al usar algun objeto del destructuring, es porque donde estas usando el useContext no está presente el Provider.

En caso de que sí, es porque tienes el provider y el useContext en el mismo componente, y estos van separados.

Por si a alguien le interesa os dejo como implemente el provider y el use context con TypeScript, no se si será la mejor manera pero por lo que estuve viendo es una de las manera de hacerlo:

Primero de todo cuando hacemos el createContext se le pasa una inteface con todo lo que contendrá el context, el tipado si os fijáis tiene que ser con opción a null puesto que se inicializa a null.

export type TodoContextInterface = {
  createTodoValue: string;
  setTodoValue: React.Dispatch<React.SetStateAction<string>>;
  addTodo: () => void;
  completedTodos: number;
  totalTodos: number;
  searchValue: string;
  setSearchValue: React.Dispatch<React.SetStateAction<string>>;
  toogleFilterComplete: () => void;
  filterComplete: boolean;
  todosFiltered: Todo[];
  completeTodos: (text: string) => void;
  deleteTodos: (text: string) => void;
}

const TodoContext = createContext<TodoContextInterface | null>(null);

Seguido creamos la función todoProvider que cambia muy poco de la que se hace en JavaScript, solo que en este caso creo el objeto y luego se lo paso al value.

const context: TodoContextInterface = {
    createTodoValue,
    setTodoValue,
    addTodo,
    completedTodos,
    totalTodos,
    searchValue,
    setSearchValue,
    toogleFilterComplete,
    filterComplete,
    todosFiltered,
    completeTodos,
    deleteTodos,
  }

  return (
    <TodoContext.Provider value={context}>
      {props.children}
    </TodoContext.Provider>
  );

La implementación del TodoProvider en App es exactamente igual.

function App() {
  
  return (
    <TodoProvider>
      <AppUI />
    </TodoProvider>
  );
}

export default App;

A la hora de extraer los datos con use context si cambia algo porque tenemos que hacer comprobación de que no vengan a null puesto que los inicializamos a null. Por ello en lugar de hacerlo una y otra vez en los componentes creé un customHook que extrae los datos y los devuelve cuando lo llamas. Este hook puede hacerse mas reutilizable pasándole parámetros pero como no es muy grande la aplicación tampoco lo vi necesario.

import { useContext } from "react";
import { TodoContext } from "../context/todoContext";

function useTodoContext() {
  const todoContext = useContext(TodoContext);
  
  if(todoContext === null) throw new Error('Todo need a context');

  return todoContext;
}

export { useTodoContext };

Y aquí estaría la implementación en los componentes, en los cuales llamo a mi customHook.

import { useTodoContext } from "../hooks/useTodoContext";

export default function TodoCounter() {
  const { completedTodos, totalTodos } = useTodoContext();

  return (
    <h2 className="subtitle">Completed {completedTodos} to {totalTodos} tasks</h2>
  );
}

Espero os sirva de ayuda y si alguien sabe una manera mejor de implementarlo por favor compartir puesto que no siempre es fácil encontrar contenido en TypeScript.

Friendly reminder 😁 Al momento de usar la destructuracion podemos renombrar las propiedades para utilizarlas con el nombre tal cual estaban:

const { totalTodos: total, completedTodos: completed } = React.useContext(TodoContext);

explica bien pero. a veces marea con tanta emocion que le coloca por decirlo asi

Me encantó este hook.
Ya decia yo que el consumer era un poquito engorroso xd
Al menos no tanto como pasar props como por 5 niveles pero ya que existe useContext sin duda empezaré a implementarlo en mis proyectos :3

.
Es como declarar estados globales.

Single responsibility, excelente curso

Eso de cortar sin miedo es peligroso porque te salen mil errores, pero una vez los solucionas te siente un PRO hacker+ de la computación

Aprovechando esta maravilla de useContext modifiqué mi TodoCounter asi
.

.
Si ya hay TODOs aparece el contador normal

function TodoCounter() {
    const {totalTodos, completedTodos, loading, searchedTodos } = useContext(TodoContext)
    if (!loading && !searchedTodos.length) {
        return (
            <h2 className='TodoCounter'>Crea tu primer TODO 💪</h2>
        )
    } else {
        return (
            <h2 className='TodoCounter'>Has completado {completedTodos} de {totalTodos} TODOs 💪</h2>
        )
    }
}

venia toda seria siguiente el video y la emoción del 4:31 me hizo estallar de risa 🤣

“No le tengas miedo a borrar”.
No es miedo, es “precaución” 😂

En esta clase no rompí todo el código al cortar, copiar, pegar. Voy a tomar eso como una victoria 😄

Otra manera de solucionar el problema presentado en el 5:30 es no cambiar las variables total y complete y usar el destructuring assignment para asignarles los valores que vienen de totalTodos y completedTodos.

import React from 'react';
import './TodoCounter.css';
import { TodoContext } from '../TodoContext';

function TodoCounter() {
    const { totalTodos: total, completedTodos: completed } = React.useContext(TodoContext);
    return (
        <h2 className="TodoCounter">Has completado {completed} de {total} TODOs</h2>
    )
}

export { TodoCounter };

useContext acepta un objeto de contexto (el valor devuelto de React.createContext) y devuelve el valor de contexto actual. El valor actual del contexto es determinado por la propiedad value del <MyContext.Provider> ascendentemente más cercano en el árbol al componente que hace la llamada.

Aunque si quieren un tip, yo lo que hice fue renombrar esas variables y dejandolas como estan, no afecta su funcionamiento de toda el componente.

const {totalTodos: total, completedTodos: completed} = React.useContext(TodoContext);
  return (
      <h2 className="TodoCounter">Has completado {completed} de {total} TODOs</h2>
  );

De hecho esto es una mejor manera de consumir el context, es mas legible.

Llevaba un tiempo trabajando con React, lo había aprendido a tropezones y prueba y error hasta que le cogí gusto. Este curso me está abriendo mucho los ojos y haciendo cogerle mucho más gusto y buenas practicas :3

Mi esructura de carpetas por el momento

.
Referencia: https://blog.webdevsimplified.com/2022-07/react-folder-structure/

Cuando emplear React Context!

  • Estado global
  • Tema
  • Configuacion
  • Autenticacion
  • Configuracion de usuario
  • Lenguaje preferido
  • Coleccion de servicios

Cada vez más fan de React

Brutalmente hermoso. Cada vez me enamoro más de React ❤️

Agregue esta condición para que no muestre el crea tu primer todo si es que estas buscando y no encuentra nada en con el patero. {!loading && !searchedTodos.length && !searchValue && (

Sabían
que es una buena practica usar su contexto a través de un React Hook ?

const useTodoContext = () => {
	// Evaluamos si existe el contexto actual, sino lanzamos un error.
  const context = useContext(TodoContext)
  if (context === undefined) {
    throw new Error('useTodoContext must be used within a TodoContextProvider')
  }
  return context
}

de esta manera tienes más control a la hora de consumir tus contextos (que cuando tu aplicación crezca pueden llegar a ser MUCHISIMOS.

Más información en el blog del genio Kent C Dodds

Todo esto es genial, ya quiero desarrollar apps con React jsjs

Así quedó mi Context:

import { createContext } from 'preact'
import { useState } from "preact/hooks"

import { useLocalStorage } from "../hooks/useLocalStorage"

const TodoContext = createContext()

const TodoProvider = (props) => {

    const [todos, setTodos] = useLocalStorage("TODOS_V1", "[]")
    const [searchValue, setSearchValue] = useState("")

    // Counter scope
    const completed = todos.filter(todo => !!todo.completed).length
    const total = todos.length

    // List scope
    const filteredTodos = todos.filter(todo => todo.text.toLowerCase().includes(searchValue.toLowerCase()))
    let searchedTodos = todos
    if (searchValue.length >= 1) searchedTodos = filteredTodos

    const toggleComplete = (id) => {
        let newTodos = [...todos]
        let selectedTodo = todos.findIndex(todo => todo.id === id)

        newTodos[selectedTodo].completed = !newTodos[selectedTodo].completed;

        setTodos(newTodos)
    }

    const handleDelete = (id) => {
        setTodos(
            todos.filter(todo => todo.id != id)
        )
    }

    const values = {
        // Counter
        completed,
        total,
        // Search
        searchValue,
        setSearchValue,
        // List
        searchedTodos,
        toggleComplete,
        handleDelete,
    }

    return (
        <TodoContext.Provider value={values}>
            {props.children}
        </TodoContext.Provider>
    )
}

export { TodoContext, TodoProvider }

😄

si con este hook el codigo es mas legible y hasta facil de hacer.

Estuve batallando horas y horas con mi aplicación, la troné y no tenía idea de porque no estaba funcionando, yo le hechaba la culpa a la función donde usaba useEffect, puesto que se saltaba esa función especificamente y no entraba a realizar la llamada de la API, busque por cielo mar y tierra sin saber por qué funcionaba así, ahora veo que seguramente cometí algún error de sintaxis usando el componente Consumer.
Solo aplique el cambio usando el Hook useContext y listo, asunto arreglado, no quería ver esta clase del curso porque no quería avanzar sin resolver ese problema.
Esto es magia pura jajaja (obvio es broma), me está costando un poco acostumbrarme a los hooks, pero veo que son extremadamente útiles.

Pero que belleza la de useContext. Me encanta jaja

Como pocas clases me da cierta ansidad saber que se va a terminar, es como cuando la serie se pone buena y no queres que termine, que tremendo profesor que es JuanDC

Noté que al hacer una búsqueda que no genere una coincidencia con los TODOS existentes, aparace el mensaje “Crea tu primer TODO” al igual que cuando no tenemos ningún todo en la lista. Mejoraría el código si al componente AppUI le pasó también la propiedad totalTodos

  const {
    error,
    loading,
    searchedTodos,
    completeTodo,
    deleteTodo,
    totalTodos
  } = React.useContext(TodoContext);

Y luego en el return, dentro del componente TodoList agrego una nueva línea de lógica para mostrar un mensaje distinto cuando la búsqueda no arroje resultados pero sí existen TODO’s en la lista:

            {(!loading && !searchedTodos.length && !!totalTodos) && <p>¡No hay ningún TODO que coincida con tu búsqueda!</p>}
            {(!loading && !totalTodos) && <p>¡Crea tu primer TODO!</p>}

También tuve que agregar la condición de que la lista esté vacía para que aparezca el mensaje de crear el primer TODO.

No sé por qué todo lo que se ha desestructurado en objetos no me funciona 😦

Ojala, pudiera llegar al nivel del Prof… no le corre… no funciona… y de una vez observa el código y encuentra el error… Mientras yo: horas y aun nada…

Por si dejan de aparecer los componentes
Solo es poner un ‘return’ en la función del Consumer

Buenisimo!

Hola Juan, cada que haces una manera diferente de cómo usar x o y cosa explota la App 😦

Pregunta de examen:
¿Cómo usamos React Context con la sintaxis de React Hooks?