Here is an example of props drilling. In this application, we have access to theme data that we want to pass as a prop to all of our app’s components. As you can see, however, the direct children of App
, such as Header
, also have to pass the theme data down using props.
export default function App({ theme }) {
return (
<>
<Header theme={theme} />
<Main theme={theme} />
<Sidebar theme={theme} />
<Footer theme={theme} />
</>
);
}
function Header({ theme }) {
return (
<>
<User theme={theme} />
<Login theme={theme} />
<Menu theme={theme} />
</>
);
}
theme
prop through multiple components that don’t immediately need it.Header
component doesn’t need theme
other than to pass it down to its child component. In other words, it would be better for User
, Login
and Menu
to consume the theme data directly.createContext
method.value
prop.In our App
, let’s pass down our own name using Context and read it in a nested component: User
.
import React from 'react';
export const UserContext = React.createContext();
export default function App() {
return (
<UserContext.Provider value="Reed">
<User />
</UserContext.Provider>
)
}
function User() {
return (
<UserContext.Consumer>
{value => <h1>{value}</h1>}
{/* prints: Reed */}
</UserContext.Consumer>
)
}
Let’s break down what we are doing, step-by-step:
App
component, we are creating context with React.createContext()
and putting the result in a variable, UserContext
. In almost every case, you will want to export it as we are doing here because your component will be in another file. Note that we can pass an initial value
to our value prop when we call React.createContext()
.App
component, we are using UserContext
. Specifically UserContext.Provider
. The created context is an object with two properties: Provider
and Consumer
, both of which are components. To pass our value down to every component in our App, we wrap our Provider component around it (in this case, User
).UserContext.Provider
, we put the value that we want to pass down our entire component tree. We set that equal to the value
prop to do so. In this case, it is our name (here, Reed).User
, or wherever we want to consume (or use) what was provided on our context, we use the consumer component: UserContext.Consumer
. To use our passed down value, we use what is called the render props pattern. It is just a function that the consumer component gives us as a prop. And in the return of that function, we can return and use value
.Instead of using render props, we can pass the entire context object to React.useContext()
to consume context at the top of our component:
import React from 'react';
export const UserContext = React.createContext();
export default function App() {
return (
<UserContext.Provider value="Reed">
<User />
</UserContext.Provider>
)
}
function User() {
const value = React.useContext(UserContext);
return <h1>{value}</h1>;
}
useContext
hook is that it makes our components more concise and allows us to create our own custom hooks.useContext
hook, depending on which pattern you prefer.Here is an application with a nested Avatar
component that requires two props username
and avatarSrc
from the App
component.
export default function App({ user }) {
const { username, avatarSrc } = user;
return (
<main>
<Navbar username={username} avatarSrc={avatarSrc} />
</main>
);
}
function Navbar({ username, avatarSrc }) {
return (
<nav>
<Avatar username={username} avatarSrc={avatarSrc} />
</nav>
);
}
function Avatar({ username, avatarSrc }) {
return <img src={avatarSrc} alt={username} />;
}
Since only the top most component, App
, needs to know about the Avatar
component, we can create it directly within App
. This allows us to pass down a single prop, avatar
, instead of two:
export default function App({ user }) {
const { username, avatarSrc } = user;
const avatar = <img src={avatarSrc} alt={username} />;
return (
<main>
<Navbar avatar={avatar} />
</main>
);
}
function Navbar({ avatar }) {
return <nav>{avatar}</nav>;
}
Source:https://www.freecodecamp.org/news/react-context-for-beginners/