Getting started with React: Managing state using Context API

Storing a central state- the need

Case A, independent parents with no direct link

Case B, common parent and ‘siblings’ at different levels

Font colour changing for the comments section

The Context API

create-react-app posts
import React, { Component } from 'react';
import './App.css';
class App extends Component {
render () {
return (
<div className="App">
<h1>Posts</h1>
</div>
);
}
}
export default App;
class App extends Component {
state = {
darkTheme: false
};
render () {
return (
<div className="App">
<h1>Posts</h1>
</div>
);
}
}
https://jsonplaceholder.typicode.com/posts
import React, { Component } from 'react';class Posts extends Component {
state = {
posts: []
}
componentDidMount() {
fetch('https://jsonplaceholder.typicode.com/posts')
.then(response => response.json())
.then(responseData => this.setState({ posts: responseData }));
}
render() {
return (
<div>List of Posts</div>
);
}
};
export default Posts;
import Posts from './Containers/Posts/Posts';
class App extends Component {
state = {
darkTheme: false,
};
render() {
return (
<div className="App">
<h1>Posts</h1>
<Posts />
</div>
);
}
}
Showing a text for now where our list will be populate
Network call showing the data we received
src/Components/Post/Post.js
import React from 'react';const post = (props) => {
return (
<div style={{
border: '1px solid #ccc',
borderRadius: '5px',
padding: '10px',
width: '20%',
margin: '10px'
}}>
<h3>{props.title}</h3>
<p>{props.body}</p>
</div>
);
};
export default post;
import Post from '../../Components/Post/Post';
...render() {
return (
<div
style={{
display: 'flex',
justifyContent: 'center',
flexWrap: 'wrap'
}}
>
{
// check if posts exist
this.state.posts.length
? (
this.state.posts.map(post => (
// display each post
<Post
key={post.id}
title={post.title}
body={post.body}
/>

))
)
: (
// show message if no posts
<h3>No Posts</h3>
)
}
</div>
);
}
...
import React from 'react';const button = (props) => {
return (
<div>
<button
style={{
padding: '10px',
border: '1px solid #ccc',
background: '#4a4a4a',
color: '#fff'
}}
onClick={props.clickHandler}
>
{props.label}
</button>
</div>
);
};
export default button;
import Button from './Components/Button/Button';
...  themeChangeHandler = () => {
this.setState({ darkTheme: !this.state.darkTheme });
};
render() {
return (
<div className="App">
<h1>Posts</h1>
<Button
label="Change Theme"
clickHandler={this.themeChangeHandler}
/>

<Posts />
</div>
);
}
...
Page with our theme toggle button component

Step 1: creating a context

import React from 'react';const ThemeContext = React.createContext({ darkTheme: false });export default ThemeContext;

Step 2: Creating a Provider

import ThemeContext from './Context/ThemeContext';
...render() {
return (
<div className="App">
<h1>Posts</h1>
<ThemeContext.Provider
value={
{ darkTheme: this.state.darkTheme }
}
>

<div>
<Button
label="Change Theme"
clickHandler={this.themeChangeHandler}
/>
<Posts />
</div>
</ThemeContext.Provider>
</div>
);
}
...

Step 3: Consuming the value provided by the Provider

import ThemeContext from '../../Context/ThemeContext';
...const post = (props) => {
return (
<ThemeContext.Consumer>
{value => (
<div style={{
border: '1px solid #ccc',
borderRadius: '5px',
padding: '10px',
width: '20%',
margin: '10px'
}}>
<h3>{props.title}</h3>
<p>{props.body}</p>
</div>
)}
</ThemeContext.Consumer>
);
};
...
<MyContext.Consumer>
{value => /* render something based on the context value */}
</MyContext.Consumer>
const post = (props) => {
return (
<ThemeContext.Consumer>
{value => (
<div style={{
border: '1px solid #ccc',
borderRadius: '5px',
background: value.darkTheme
? '#4a4a4a'
: '#fff',
color: value.darkTheme
? '#fff'
: '#000',
padding: '10px',
width: '20%',
margin: '10px'
}}>
<h3>{props.title}</h3>
<p>{props.body}</p>
</div>
)}
</ThemeContext.Consumer>
);
};
import ThemeContext from '../../Context/ThemeContext';
class Posts extends Component {
state = {
posts: []
}
static contextType = ThemeContext; componentDidMount() {
fetch('https://jsonplaceholder.typicode.com/posts')
.then(response => response.json())
.then(responseData => this.setState({ posts: responseData }));
}
...}
class Posts extends Component {
state = {
posts: []
}
static contextType = ThemeContext; componentDidMount() {
fetch('https://jsonplaceholder.typicode.com/posts')
.then(response => response.json())
.then(responseData => this.setState({ posts: responseData }));
}
render() {
return (
<div
style={{
display: 'flex',
justifyContent: 'center',
flexWrap: 'wrap',
background: this.context.darkTheme
? '#ccc'
: '#fff'
}}
>
{
// check if posts exist
this.state.posts.length
? (
this.state.posts.map(post => (
// display each post
<Post
key={post.id}
title={post.title}
body={post.body}
/>
))
)
: (
// show message if no posts
<h3>No Posts</h3>
)
}
</div>
);
}
};
...class Posts extends Component {static contextType = ThemeContext;render() {
return (
<div
style={{
display: 'flex',
justifyContent: 'center',
flexWrap: 'wrap',
background: this.context.darkTheme
? '#ccc'
: '#fff'
}}
>
{
// we will now pick posts from context
this.context.posts.length
? (
this.context.posts.map(post => (
// display each post
<Post
key={post.id}
title={post.title}
body={post.body}
/>
))
)
: (
// show message if no posts
<h3>No Posts</h3>
)
}
</div>
);
}
};
...
class App extends Component {
state = {
darkTheme: false,
posts: [],
};
themeChangeHandler = () => {
this.setState({ darkTheme: !this.state.darkTheme });
};
postDeleteHandler = (id) => {
const updatedPosts = (
this.state
.posts
.filter(post => post.id !== id)
);
this.setState({posts: updatedPosts});
};
componentDidMount() {
fetch('
https://jsonplaceholder.typicode.com/posts')
.then(response => response.json())
.then(responseData => this.setState({ posts: responseData }));
}
render() {
return (
<div className="App">
<h1>Posts</h1>
<ThemeContext.Provider value={
{
darkTheme: this.state.darkTheme,
posts: this.state.posts,
deleteHandler: this.postDeleteHandler
}
}>
<div>
<Button
label="Change Theme"
clickHandler={this.themeChangeHandler}
/>
<Posts />
</div>
</ThemeContext.Provider>
</div>
);
}
}
  • added our posts to the state,
  • created a handler method to delete any posts which match a given id (which we get with our mock response)
  • added a componentDidMount( ) lifecycle hook to make our API call and update the state
  • distributed our posts and the delete handler via our ThemeContext Provider’s value prop
import React from 'react';
import ThemeContext from '../../Context/ThemeContext';
import Button from '../Button/Button';
const post = (props) => {
return (
<ThemeContext.Consumer>
{value => (
<div style={{
border: '1px solid #ccc',
borderRadius: '5px',
background: value.darkTheme
? '#4a4a4a'
: '#fff',
color: value.darkTheme
? '#fff'
: '#000',
padding: '10px',
width: '20%',
margin: '10px'
}}>
<h3>{props.title}</h3>
<p>{props.body}</p>
<Button
label="Delete"
clickHandler={() => value.deleteHandler(props.postId)}
/>

</div>
)}
</ThemeContext.Consumer>

);
};
export default post;
...  <Post
key={post.id}
title={post.title}
body={post.body}
postId={post.id}
/>
...
Posts being delete for different themes
Happy Coding!

--

--

--

Building things, breaking things, learning things

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

Why you should use NestJS for your next project

Working with .split() and .join() in JavaScript

Map().Filter().Reduce()

How to correctly mock Moment.js/dates in Jest

Micro-FEs Simplified

Fun with “scanf” in c

What the heck are JS variables?

Writing custom TypeScript ESLint rules: How I learned to love the AST

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Arun Kant Pant

Arun Kant Pant

Building things, breaking things, learning things

More from Medium

React Components

React 18 Release Candidate is Released | Learn React 18, it's APIs and SSR Improvements

Programinja | thealiraza | React 18 Release Candidate is finally here.

Hooked on React

React Dropzone and upload images Part 9 Render the data from firestore