React useContext
El Context en React es una manera de evitar el prop drilling y pasar props entre componentes.
Se divide en dos partes, el Provider (el que ofrece el contexto) y el Consumer (el que lo recibe)
Provider
Es necesario crear una constante que sea igual a createContext() y envolver la aplicación (o los componentes que queramos que tengan acceso al contexto) con el Provider.
Recuerda
¡Debes crearlo FUERA del componente y EXPOTARLO! En caso contrario estaríamos recreando el contexto cada vez que el componente se renderiza.
Es habitual que el Context sea un objeto ya que es una forma sencilla de poder enviar varios valores al mismo tiempo. En este caso enviamos un state y su setter function.
export const UserContext = createContext();
function App() {
const [user, setUser] = useState(loquesea)
return (
<UserContext.Provider value={{user, setUser}}>
<AccountDropdown />
<CourseIndexLayout />
</UserContext.Provider>
);
}Consumer
El consumer simplemente debe importar el contexto y usar el hook useContext() para recibirlo.
import UserContext from './App'
function ModuleLessons() {
const {user, setUser} = React.useContext(UserContext);
return <>User email: {user?.email || "Not provided"}</>;
}Buenas prácticas
Let’s suppose we have 3 different pieces of state we want to pass through context. We set it up with 3 different context providers, like so:
export const UserContext = React.createContext();
export const ThemeContext = React.createContext();
export const PlaybackRateContext = React.createContext();
function App() {
const [user, setUser] = React.useState(null);
const [theme, setTheme] = React.useState('light');
const [playbackRate, setPlaybackRate] = React.useState(1);
return (
<UserContext.Provider value={user}>
<ThemeContext.Provider value={theme}>
<PlaybackRateContext.Provider value={playbackRate}>
<Homepage />
</PlaybackRateContext.Provider>
</ThemeContext.Provider>
</UserContext.Provider>
);
}It often surprises developers that we’d want to use multiple contexts. Wouldn’t it be simpler to group them all together, like this?
export const AppContext = React.createContext();
function App() {
const [user, setUser] = React.useState(null);
const [theme, setTheme] = React.useState('light');
const [playbackRate, setPlaybackRate] = React.useState(1);
return (
<AppContext.Provider value={{ user, theme, playbackRate }}>
<Homepage />
</AppContext.Provider>
);
}While this might look simpler at first glance, there are two reasons why it’s an established best practice to create individual contexts for each discrete concern:
There are potential performance benefits (we’ll learn more about this later in the course).
It can improve code readability.
That second point might be surprising: after all, the code seems more readable with a single AppContext, doesn’t it?
Well, here’s the thing: this example is wildly unrealistic. In reality, each concern often has a bunch of other stuff.
Let’s look at a more realistic example:
import React from 'react';
import useSWR from 'swr';
import { COLORS } from './constants';
import Homepage from './Homepage';
export const UserContext = React.createContext();
export const ThemeContext = React.createContext();
export const PlaybackRateContext = React.createContext();
const ENDPOINT =
'https://jor-test-api.vercel.app/api/get-current-user';
async function fetcher(endpoint) {
const response = await fetch(endpoint);
const json = await response.json();
if (!json.ok) {
throw json;
}
return json.user;
}
function App() {
const {
data: user,
error: userError,
mutate: mutateUser,
} = useSWR(ENDPOINT, fetcher);
const [theme, setTheme] = React.useState(() => {
return window.localStorage.getItem('color-theme') || 'light';
});
const [playbackRate, setPlaybackRate] = React.useState(1);
React.useEffect(() => {
window.localStorage.setItem('color-theme', theme);
}, [theme]);
const toggleTheme = React.useCallback(() => {
setTheme((currentTheme) => {
return currentTheme === 'light' ? 'dark' : 'light';
});
}, []);
const colors = COLORS[theme];
const logOut = React.useCallback(() => {
mutateUser({
user: null,
});
}, [mutateUser]);
const editProfile = React.useCallback((newData) => {
mutateUser({
user: {
...user,
...newData
},
});
}, [user, mutateUser]);
const resetPlaybackRate = React.useCallback(() => {
setPlaybackRate(1);
}, []);
return (
<UserContext.Provider value={{ user, logOut, editProfile }}>
<ThemeContext.Provider value={{
theme,
toggleTheme,
colors,
}}>
<PlaybackRateContext.Provider
value={{
playbackRate,
setPlaybackRate,
resetPlaybackRate,
}}
>
<Homepage />
</PlaybackRateContext.Provider>
</ThemeContext.Provider>
</UserContext.Provider>
);
}
export default App;