Creating and providing a context
To create the Provider, we first create a new Context (in our case, we want it to store the posts, hence the constant name) outside of any component:
import { createContext, useEffect, useState } from "react";
// 1) Create new context
const PostContext = createContext();
The PostContent starts with a capital letter, as it is a component.
Inside the parent component, we then wrap the JSX into the context’s Provider:
function App() {
{/* other stuff */}
return (
// 2) Provide value to child components
<PostContext.Provider
value={{
posts: searchedPosts,
onAddPost: handleAddPost,
onClearPosts: handleAddPost,
searchQuery,
setSearchQuery,
}}
>
<section>
<button
onClick={() => setIsFakeDark((isFakeDark) => !isFakeDark)}
className="btn-fake-dark-mode"
>
{isFakeDark ? "☀️" : "🌙"}
</button>
<Header
posts={searchedPosts}
onClearPosts={handleClearPosts}
searchQuery={searchQuery}
setSearchQuery={setSearchQuery}
/>
<Main posts={searchedPosts} onAddPost={handleAddPost} />
<Archive onAddPost={handleAddPost} />
<Footer />
</section>
</PostContext.Provider>
);
}
Note, that we need to pass the value property into the <PostContext.Provider>,
which contains an object of all props, that we originally pass down to the deeper
components here.
Hint
Practically, it is cleaner to provide different contexts for different purposes.
Here, we may only use the PostContext for post props and create a second
SearchContext for the search related props. Though we put them into one here.
Consuming the context
First, lets remove all passed down props from the component’s JSX and inside the receiving component’s arguments.
function App() {
{/* other stuff */}
return (
// 2) Provide value to child components
<PostContext.Provider
value={{
posts: searchedPosts,
onAddPost: handleAddPost,
onClearPosts: handleAddPost,
searchQuery,
setSearchQuery,
}}
>
<section>
<button
onClick={() => setIsFakeDark((isFakeDark) => !isFakeDark)}
className="btn-fake-dark-mode"
>
{isFakeDark ? "☀️" : "🌙"}
</button>
<Header />
<Main />
<Archive />
<Footer />
</section>
</PostContext.Provider>
);
}
If a component requires a certain prop, the removed prop must be replaced by calling
the useContext hook (here: Header):
import { createContext, useContext, useEffect, useState } from "react";
function Header() {
// 3) Consuming the context value
const { onClearPosts } = useContext(PostContext);
return (
<header>
<h1>
<span>OOO</span>The Atomic Blog
</h1>
<div>
<Results />
<SearchPosts />
<button onClick={onClearPosts}>Clear posts</button>
</div>
</header>
);
}
The useContext(PostContext) hook returns the entire content of the value
object from the Provider, so we can destructure any prop that we need at this point
(here: onClearPosts).