Introducción al curso avanzado de React

1

Qué necesitas para este curso y qué aprenderás sobre React.js

2

Proyecto y tecnologías que usaremos

Preparando el entorno de desarrollo

3

Clonando el repositorio e instalando Webpack

4

Instalación de React y Babel

5

Zeit es ahora Vercel

6

Linter, extensiones y deploy con Now

Creando la interfaz con styled-components

7

¿Qué es CSS-in-JS?

8

Creando nuestro primer componente: Category

9

Creando ListOfCategories y estilos globales

10

Usar información real de las categorías

11

Creando PhotoCard y usando react-icon

12

SVGR: de SVG a componente de ReactJS

13

Creando animaciones con keyframes

Hooks

14

¿Qué son los Hooks?

15

useEffect: limpiando eventos

16

useCategoriesData

17

Usando Intersection Observer

18

Uso de polyfill de Intersection Observer e imports dinámicos

19

Usando el localStorage para guardar los likes

20

Custom Hooks: useNearScreen y useLocalStorage

GraphQL y React Apollo

21

¿Qué es GraphQL y React Apollo? Inicializando React Apollo Client y primer HoC

22

Parámetros para un query con GraphQL

23

Usar render Props para recuperar una foto

24

Refactorizando y usando variables de loading y error

25

Usando las mutaciones con los likes

Reach Router

26

¿Qué es Reach Router? Creando la ruta Home

27

Usando Link para evitar recargar la página

28

Creando la página Detail

29

Agregando un NavBar a nuestra app

30

Estilando las páginas activas

31

Rutas protegidas

Gestión del usuario

32

Introducción a React.Context

33

Creación del componente UserForm; y Hook useInputValue

34

Estilando el formulario

35

Mutaciones para registro

36

Controlar estado de carga y error al registrar un usuario

37

Mutaciones para iniciar sesión

38

Persistiendo datos en Session Storage

39

Hacer like como usuario registrado

40

Mostrar favoritos y solucionar fetch policy

41

Cerrar sesión

Mejores prácticas, SEO y recomendaciones

42

Últimos retoques a las rutas de nuestra aplicación

43

React Helmet

44

Midiendo el performance de nuestra app y usando React.memo()

45

React.lazy() y componente Suspense

46

Usando PropTypes para validar las props

47

PWA: generando el manifest

48

PWA: soporte offline

49

Testing con Cypress

Conclusiones

50

¡Felicidades!

Aún no tienes acceso a esta clase

Crea una cuenta y continúa viendo este curso

Curso de React Avanzado

Curso de React Avanzado

Miguel Ángel Durán

Miguel Ángel Durán

Introducción a React.Context

32/50
Recursos

Context API es una característica que tiene React para poder pasar datos en nuestra aplicación sin necesidad de usar las Props.

Para crear un contexto vamos a importar el método createContext de la librería React. El contexto que creemos no va a dejar de ser un componente de React, por ello debe llevar mayúscula al inicio.

El Context que creemos nos va a proporcionar 2 componentes:

  • Provider: componente que debe envolver a nuestra aplicación.
  • Consumer: nos va a permitir acceder a las render props que declaremos en el Provider.

Aportes 28

Preguntas 4

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad? Crea una cuenta o inicia sesión.

Si lo hacen como el profe, en la clase siguiente, tendran que pasar al componente hijo por medio de props como normalmente se hace, pero que tal si tuviesemos que pasarselo a un nivel de profundidad de 3,4,5 nodos o mas ?. De la manera en que lo realice apoyandome de un blog, podemos acceder a un dispatch el cual nos permite actualizar nuestro isAught (ó valores que creemos en el initialState en useReducer), sin importar la profundidad, no abrá necesidad de pasarlo de hijo en hijo, simplemente lo usamos donde lo necesitamos y deseamos usar.

Les dejo la fuente de donde me ayudé, recomiendo mucho entender primero los conceptos que utilizamos (useReducer, useContext, createContext)

Fuente: https://medium.com/simply/state-management-with-react-hooks-and-context-api-at-10-lines-of-code-baf6be8302c
Mi context.jsx


import React, { createContext, useContext, useReducer } from 'react';

export const StateContext = createContext();

export const StateProvider = ({ reducer, initialState, children }) => (

  <StateContext.Provider value={useReducer(reducer, initialState)}>
    {children}
  </StateContext.Provider>
);

export const useStateValue = () => useContext(StateContext);

Otra alternativa para usar el Consumer del Context es usar el hook useContext

Ejemplo

import React,{ useContext } from 'react'
import Context from 'ruta' //importamos nuestro contexto

const miComponente = () => {
  //Le tendremos que pasar el contexto al hook,y de esa forma accederemos a todos los atributos que tiene
  const { isAuth,setAuth } = useContext(Context)
  const handleAuth = () => setAuth()

  return (
	<button onClick={handleAuth}>Iniciar sesion</button>
  )
}

Método actualizado utilizando useContext:
.
Creamos nuestro contexto:
src/context/AppContext.js

import { createContext, useState } from 'react'

export const AppContext = createContext()

export const AppProvider = ({ children }) => {
  const [isAuth, setIsAuth] = useState(false)
  const user = { isAuth }
  const login = () => {
    setIsAuth(true)
  }

  return (
    <AppContext.Provider value={{user, login}}>
      {children}
    </AppContext.Provider>
  )
}

Luego envolvemos nuestra aplicación en el contexto:
src/index.js

...
...
import { AppProvider } from './context/AppContext'

...
...

ReactDOM.render(
  ...
    <AppProvider>
      <App />
    </AppProvider>
  ...,
  document.getElementById('app')
)

Haciendo uso del contexto desde nuestras rutas
src/Routes/index.js

import { useContext } from 'react'
...
...
...

import { AppContext } from '../context/AppContext'

export const Routes = () => {
  const { user: { isAuth } } = useContext(AppContext)
  return (
    <Router>
      <Logo />
      <ListOfCategories />
      <Switch>
        ...
        ...
        ...
        <Route exact path='/favs' render={() => isAuth ? <Favorite /> : <Redirect to='/register' />} />
        <Route exact path='/user' render={() => isAuth ? <User /> : <Redirect to='/register' />} />
        <Route exact path='/register' component={Register} />
      </Switch>
      <Navbar />
    </Router>
  )
}

Haciendo uso del contexto desde el componente Register
src/pages/Register.js

import { useContext } from 'react'
import { useHistory } from 'react-router-dom'
import { AppContext } from '../context/AppContext'

export const Register = () => {
  const history = useHistory()
  const { login } = useContext(AppContext)

  const handleSubmit = e => {
    e.preventDefault()
    login(e)
    history.push('/user')
  }

  return (
    <section>
      <h1>Register</h1>
      <form onSubmit={handleSubmit}>
        <button>Iniciar Sesión</button>
      </form>
    </section>
  )
}

Al finalizar, asi se vería el estado de nuestra aplicación desde el Provider de contexto:

Campaña por una actualización de este curso 😄

Un resúmen de cómo funciona nuestro Context

1, El Provider envuelve nuestra aplicación haciendo que sea accesible el Consumer desde cualquier parte de la misma, éso es posible por el acceso a la propiedad del children desde el Context.js.

const Provider = ({ children }) => {

const value = {objetoConLasValuesDefinidas}

  return (
    <Context.Provider value={value}>
      {children}
    </Context.Provider>
  );
};

.
2, Dentro del provider hacemos uso de una prop llamada value, donde guardamos nuestros métodos y atributos que queramos usar en nuestras render props.

  const value = {
    isAuth,
    activateAuth: () => {
      setIsAuth(true);
    },
  };

.
3, El Context.Consumer (por defecto) genera una render prop donde la función children tiene acceso al value definido dentro de nuestro Context.Provider

export default {
  Provider,
  Consumer: Context.Consumer,
};

.
4, Para hacer un efecto, tan sólo tenemos que llamar a algún metodo definido en el contexto mediante render props.

<Context.Consumer>
        {
          ({ isAuth }) => (isAuth ? (
            <Router>
              <Favs path="/favs" />
              <User path="/user" />
            </Router>
          ) : (
            <Router>
              <NotRegisteredUser path="/favs" />
              <NotRegisteredUser path="/user" />
            </Router>
          ))
        }
      </Context.Consumer>

Me estresa que haga tanta refactorización del código

¿Qué tal en este caso usar el hook useContext?
Podría ser un poco más limpio el código, pues así quedó mi componente <NotRegisteredUser />

export const NotRegisteredUser = () => {
  const { activateAuth } = useContext(UserAuthContext.Context)

  return (
    <Fragment>
      <RegisterMutation>
        {
          (register) => {
            const onSubmit = ({ email, password }) => {
              const input = { email, password }
              const variables = { input }
              register({ variables })
                .then(() => {
                  activateAuth()
                })
            }
            return <UserForm title='Registrarse' onSubmit={onSubmit} />
          }
        }
      </RegisterMutation>

      <UserForm title='Iniciar sesión' onSubmit={activateAuth} />
    </Fragment>
  )
}

Forma validada para useContext 2021
De esta forma si me sirvio la app
App.js

import React, {useContext} from "react";
import { BrowserRouter, Routes, Route} from "react-router-dom";

import { AppContext } from "./Context/AppContext";

//styles
import { GlobalStyle } from "./Styles/GlobalStyles";
// import { Routes } from './Routes'
import { Home } from './Pages/Home';
import { Detail } from "./Pages/Detail";
import { Favs } from "./Pages/Favs";
import { User } from "./Pages/User";
import { NotRegisteredUser } from "./Pages/NotRegisteredUser";

import { Logo } from './Components/Logo';
import { NavBar } from './Components/NavBar';

export const App = () =>{
    const  urlParams = new window.URLSearchParams(window.location.search)
    const detailId = urlParams.get('detail')
    console.log(detailId)
    const { isAuth } = useContext(AppContext)
    return (
        <BrowserRouter>
        <GlobalStyle />
        <Logo />
        <Routes>
            <Route path='/' element={<Home />} />
            <Route path='/pet/:id' element={<Home />} />
            <Route path='/detail/:id' element={<Detail />} />
            <Route path='/favs' element={isAuth ? <Favs /> : <NotRegisteredUser />}/>
            <Route path='/user'  element={isAuth ? <User /> : <NotRegisteredUser />} />

        </Routes>
        <NavBar />
        </BrowserRouter>
    )
} 

index.js

import React from 'react'
import ReactDOM from 'react-dom'
import { ApolloClient, ApolloProvider, InMemoryCache } from "@apollo/client";

import {AppProvider}  from './Context/AppContext';

import {App} from './App';

const client = new ApolloClient({
    uri: "https://petgram-server-alejandroverita-alejandroverita.vercel.app/graphql",
    cache: new InMemoryCache(),
});

ReactDOM.render(
    <AppProvider>
        <ApolloProvider client={client}>
            <App />
        </ApolloProvider>
    </AppProvider>,
    document.getElementById('app')
)

Context/AppContex.js

import React, { createContext, useState} from 'react';

const AppContext = createContext(null);

const AppProvider = (props) => {
    const [isAuth, setIsAuth] = useState(false)

    const activateAuth = () => setIsAuth(true)
  
    return (
      <AppContext.Provider value={{ isAuth, activateAuth }}>
        {props.children}
      </AppContext.Provider>
    )
}

export {
    AppContext, AppProvider
}

NotRegisteresUser.js

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

export const NotRegisteredUser = () => {
    const { activateAuth } = useContext(AppContext)
  return (
    <>
        <form onSubmit={activateAuth}>
        <button>Iniciar Sesión</button>
        </form>
    </>
  )
}

Por qué usar Context y no Redux? No conocía Context, y me parece genial no tener que implementar mas librerias.

Que poco intuivo es el tema de las render props, definitivamente los hooks son lo mejor que le pudo pasar a React!.. Con razón tanta gente migró a Vue en su momento.

Si tienen problemas para declarar el contexto, pueden usarlo de la siguiente manera:

import { createContext } from 'react';
const { Provider, Consumer } = createContext();
export { Consumer, Provider };

comparto mi manera para usar useContext:

creamos Context.js:

import React, { createContext, useState } from 'react'

export const Context = createContext(null)

export const Provider = props => {
  const [isAuth, setIsAuth] = useState(false)

  const value = {
    isAuth,
    activeAuth: () => {
      setIsAuth(true)
    }
  }

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

En index.js:

.... imports
import {Provider} from './Context'

const client = new ApolloClient({
  uri: 'https://petgram-server-24iykciv5.now.sh/graphql'
})
ReactDOM.render(
  <Provider>
    <ApolloProvider client={client}>
      <App />
    </ApolloProvider>
  </Provider>,
  document.getElementById('app')
)

En App.js:

... imports
import { Context as contexto } from './Context'

export const App = () => {
  const { isAuth, activateAuth } = useContext(contexto)

  return (
    <div>
      <GlobalStyle />
      <div className='logo__container'>
        <Logo />
      </div>
      <Router>
        <Home path='/' />
        <Home path='/pet/:id' />
        <Detail path='/detail/:detailId' />
      </Router>

      <>
        {isAuth
          ? <Router>
            <Favs path='/favs' />
            <User path='/user' />

          </Router>
          : <Router>
            <NotRegisterUser path='/favs' />
            <NotRegisterUser path='/user' />

          </Router>}
      </>
      <NavBar />
    </div>
  )
}

Y en NotRegisteredUser.js:

import React from 'react'
import {Context} from '../Context'

export const NotRegisterUser = () => (
  <Context.Consumer>
    {
      ({ activateAuth }) =>{

        return (
          <form onSubmit={activateAuth}>
            <button>Iniciar sesión</button>
          </form>
        )
      }
    }
  </Context.Consumer>
)

Codigo:

Este video del Curso Profesional de React Hooks puede ayudar a entender el concepto de Context 😃

todo esto sin redux! genial

¿Que ventaja tiene usar la api context de react sobre una externa que cumple la misma funcion como redux?

Buaaaaah el profe habla a 100km/h JAJJAJ

es magia

¿Por qué Context.Provider renderiza los otros componentes directamente cuando se usa en el index y en cambio en otras oportunidades cuando hemos usado children hemos usado una función para renderizar los componentes dentro de esos?

Una manera más moderna de hacer esto, con React Hooks, es usar useContext en el elemento en el cual usaremos el contexto. En este caso, en nuestro Context.js debemos exportar Context en lugar de Solo Provider y Consumer, ya que con el hook usaremos el contexto completo.

Contexto:

import React, { useState } from 'react';

const Context = React.createContext();

const Provider = ({ children }) => {
  const [isAuth, setIsAuth] = useState(false);

  const value = {
    isAuth,
    activeAuth: () => {
      setIsAuth(true);
    },
  };

  return (
    <Context.Provider value={value}>
      {children}
    </Context.Provider>
  );
};

export default {
  Context,
  Provider,
  Consumer: Context.Consumer,
};

Componente:

import React, { useContext } from 'react';

import { Context } from '../Context';

export const ComponentExample = () => {
	const { isAuth, activateAuth } = useContext(Context);
	
	return (
		<>
			{isAuth ? 'Estás Logueado' : 'No Estás Logueado'}
		</>
	);
};

Context API es una característica que tiene React para poder pasar datos en nuestra aplicación sin necesidad de usar las Props.

Para crear un contexto vamos a importar el método createContext de la librería React. El contexto que creemos no va a dejar de ser un componente de React, por ello debe llevar mayúscula al inicio.

El Context que creemos nos va a proporcionar 2 componentes:

Provider: componente que debe envolver a nuestra aplicación.
Consumer: nos va a permitir acceder a las render props que declaremos en el Provider.

En el ejemplo lo utiliza para crear un contexto global dentro de la aplicacion. Pero, es recomendable utilizar Context para compartir datos entre solo dos componentes que no esten relacionados?

¿Por qué se pasa la función activateAuth en vez de pasar setIsAuth directamente?

Asi quedo mi Context. con useContext

Context.js

import React, { createContext, useState } from "react";

export const UserContext = createContext();

export const UserProvider = ({ children }) => {
  const [isAuth, setIsAuth] = useState(false);

  const user = {
    isAuth,
  };

  const login = (e) => {
    e.preventDefault();
    setIsAuth(true);
  };

  return (
    <UserContext.Provider value={{ user, login }}>
      {children}
    </UserContext.Provider>
  );
};

Index.js

import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import ApolloClient from "apollo-boost";
import { ApolloProvider } from "@apollo/react-hooks";
import { UserProvider } from "./Context";

const client = new ApolloClient({
  uri: "https://petgram-vercel-two.vercel.app/graphql",
});
ReactDOM.render(
  <ApolloProvider client={client}>
    <UserProvider>
      <App />
    </UserProvider>
  </ApolloProvider>,
  document.getElementById("app")
);

NotRegisteredUser

import React, { useContext } from "react";
import { UserContext } from "../Context";

const NotRegisteredUser = () => {
  const { user, login } = useContext(UserContext);

  return (
    <div>
      <h1>NotRegisteredUser</h1>
      <form action="" onSubmit={(e) => login(e)}>
        <button>Iniciar Sesion</button>
      </form>
    </div>
  );
};

export default NotRegisteredUser;

Me pareció un poco complicada la forma como lo hace el profesor, yo lo hice de esta manera utilizando useContext

Este es mi context.js

import React, { useContext, useState } from 'react'

const AuthContext = React.createContext()

export function useAuthContext () {
  return useContext(AuthContext)
}

export function AuthProvider ({ children }) {
  const [isAuth, setIsAuth] = useState(false)
  function activateUser () {
    return setIsAuth(!isAuth)
  }

  const value = {
    isAuth,
    activateUser
  }

  return (
    <AuthContext.Provider value={value}>
      {children}
    </AuthContext.Provider>
  )
}

index.js

import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import { ApolloClient, InMemoryCache } from '@apollo/client'
import { ApolloProvider } from '@apollo/client/react'
import { AuthProvider } from './context'

const client = new ApolloClient({
  uri: 'https://petgramserver-9na7gnxdl-effrenanthony.vercel.app/graphql',
  cache: new InMemoryCache()
})

ReactDOM.render(
  <AuthProvider>
    <ApolloProvider client={client}>
      <App />
    </ApolloProvider>
  </AuthProvider>,
  document.getElementById('app')
)

NotRegisterUser.js

import React from 'react'
import { useAuthContext } from '../context'

export const NotRegisterUser = () => {
  const { activateUser } = useAuthContext()
  return (
    <form onSubmit={activateUser}>
      <button>Iniciar Sesión</button>
    </form>
  )
}

App.js

import React from 'react'
import Logo from './components/Logo'
import { Home } from './pages/Home'
import { GlobalStyles } from './styles/GlobalStyles'

import { Router } from '@reach/router'
import { Detail } from './pages/Detail'
import { NavBar } from './components/Navbar'
import { Favs } from './pages/Favs'
import { User } from './pages/User'
import { NotRegisterUser } from './pages/NotRegisterUser'
import { useAuthContext } from './context'
const App = () => {
  const { isAuth } = useAuthContext()
  const UserLogged = ({ children }) => {
    return children({ isAuth: isAuth })
  }
  return (
    <div>
      <GlobalStyles />
      <Logo />
      <Router>
        <Home path='/' />
        <Home path='/pet/:categoryId' />
        <Detail path='/detail/:detailId' />
      </Router>
      <UserLogged>
        {
          ({ isAuth }) =>
            isAuth
              ? <Router>
                <Favs path='/favs' />
                <User path='/user' />
                </Router>
              : <Router>
                <NotRegisterUser path='/favs' />
                <NotRegisterUser path='/user' />
                </Router>
        }
      </UserLogged>
      <NavBar />
    </div>
  )
}

export default App

O asi tambien puede quedar App.js sin utilizar render props

import React from 'react'
import Logo from './components/Logo'
import { Home } from './pages/Home'
import { GlobalStyles } from './styles/GlobalStyles'

import { Router } from '@reach/router'
import { Detail } from './pages/Detail'
import { NavBar } from './components/Navbar'
import { Favs } from './pages/Favs'
import { User } from './pages/User'
import { NotRegisterUser } from './pages/NotRegisterUser'
import { useAuthContext } from './context'
const App = () => {
  const { isAuth } = useAuthContext()
  return (
    <div>
      <GlobalStyles />
      <Logo />
      <Router>
        <Home path='/' />
        <Home path='/pet/:categoryId' />
        <Detail path='/detail/:detailId' />
      </Router>
        {
          isAuth
            ? <Router>
              <Favs path='/favs' />
              <User path='/user' />
              </Router>
            : <Router>
              <NotRegisterUser path='/favs' />
              <NotRegisterUser path='/user' />
              </Router>
        }
      <NavBar />
    </div>
  )
}

export default App

n

Hola, les dejo el avance de mi repositorio en Next.js:

https://github.com/danyel117/petgram-platzi/tree/privateroute