OB电竞手机版官网
For this tutorial, you should have a fair understanding of hooks. Still, before we begin, I’ll briefly discuss what they are and the hooks we’ll be using in this article.
According to the React Docs :
“ Hooks are a new addition in React 16.8. They let you use state and other React features without writing a class.”
That is basically what a React hook is. It allows us to use state, refs and other React features in our functional components.
Let us discuss the two hooks we will encounter in this article.
The
useState
Hook
The useState hook
allows us to use state
in our functional components. A
useState
hook takes the initial value of our state as the only argument, and it returns an array of two elements. The first element is our state variable and the second element is a function in which we can use the update the value of the state variable.
Let’s take a look at the following example:
import React, {useState} from "react"; function SampleComponent(){ const [count, setCount] = useState(0); }
Here,
count
is our state variable and its initial value is
while
setCount
is a function which we can use to update the value of count.
The
useContext
Hook
I will discuss this later in the article but this hook basically allows us to consume the value of a context. What this actually means will become more apparent later in the article.
Yarn Workspaces
Yarn workspaces let you organize your project codebase using a monolithic repository (monorepo). React is a good example of an open-source project that is monorepo and uses Yarn workspaces to achieve that purpose. Read a related article →
Why Do We Need The Context API?
We want to build a “theme toggler” component which toggles between light mode and dark mode for our React app. Every component has to have access to the current theme mode so they can be styled accordingly.
Normally, we would provide the current theme mode to all the components through props and update the current theme using
state
:
import React from "react"; import ReactDOM from "react-dom"; function App() { return (
{theme}
); } function Text({theme}) { return(
{theme}
); } const rootElement = document.getElementById("root"); ReactDOM.render(
, rootElement);
In the code sample above, we created a Text Component which renders an
h1
element. The color of the
h1
element depends on the current theme mode. Currently, the theme is blue. We can toggle between
blue
and
red
themes by using
state
.
We will create a state called “theme” using the
useState
hook. The
useState
hook will return the current value of the theme and a function which we can use to update the theme.
So, let us create our theme state:
const [theme, setTheme] = React.useState("blue");
We will also add a button element to our
App
component. This button will be used to toggle the themes and it needs a click event handler. So, let us write the click event handler like so:
const onClickHandler = () => { setTheme(); }
Now, we want to set the new theme to
Red
if the current theme is
Blue
, and vice versa. Instead of using an
if
statement, a more convenient way to do this is with the help of the ternary operator in JavaScript.
setTheme( theme === "red"? "blue": "red");
So now, we have written our
onClick
handler. Let’s add this button element to the
App
component:
Let us also change the value of the theme props of the Text component to the theme state.
Now, we should have this:
import React from "react"; import ReactDOM from "react-dom"; import "./styles.css"; function App() { const[theme, setTheme] = React.useState("red"); const onClickHandler = () => { setTheme( theme === "red"? "blue": "red"); } return (
); } function Text({theme}) { return(
{theme}
); } const rootElement = document.getElementById("root"); ReactDOM.render(
, rootElement);
We can now toggle between our two themes. However, if this was a much larger application, it would be difficult to use the theme in deeply nested components and the code becomes unwieldy.
Introducing The Context API
Let me introduce the Context API. According to the React documentation:
“Context provides a way to pass data through the component tree without having to pass props down manually at every level.”
For a more in-depth definition, it provides a way for you to make particular data available to all components throughout the component tree no matter how deeply nested that component may be.
Let us look at this example:
const App = () => { return(
); } const ParentComponent = (props) => (
) const Child = (props) => (
) const Grandchild = (props) => (
Theme: {props.theme}
)
In the example above, we specified the application theme using a props in the
ParentComponent
called
theme
. We had to pass that props to all components down the component tree to get it where it is needed which is the
GrandChild
component. The
ChildComponent
had nothing to do with the theme props but was just used as an intermediary.
Now, imagine the
GrandChild
component was more deeply nested than it was in the top example. We would have to pass the theme props the same way we did here which would be cumbersome. This is the problem that
Context
solves. With
Context
, every component in the component tree has access to whatever data we decide to put in our context.
Let’s Get Started With
Context
It’s time to replicate the theme toggling button we built at the beginning of the article with the Context API. This time, our theme toggler will be a separate component. We will build a
ThemeToggler
component which switches the theme of our React app using
Context
.
First, let us initialize our React app. (I prefer using
create-react-app
but you can use whatever method you prefer.)
Once you have initialized your React project, create a file called
ThemeContext.js
in your
/src
folder. You can also create a folder called
/context
and place your
ThemeContext
file in there if you want.
Now, let us move on.
Creating Your Context API
We will create our theme context in our ThemeContext.js file.
To create a context, we use
React.createContext
which creates a context object. You can pass in anything as an argument to
React.createContext
. In this case, we are going to pass in a string which is the current theme mode. So now our current theme mode is the “light” theme mode.
import React from "react"; const ThemeContext = React.createContext("light"); export default ThemeContext;
To make this context available to all our React components, we have to use a Provider. What is a Provider? According to the React documentation, every context object comes with a Provider React component that allows consuming components to subscribe to context changes. It is the provider that allows the context to be consumed by other components. That said, let us create our provider.
Go to your
App.js
file. In order to create our provider, we have to import our
ThemeContext
.
Once the
ThemeContext
has been imported, we have to enclose the contents of our
App
component in
ThemeContext.Provider
tags and give the
ThemeContext.Provider
component a props called
value
which will contain the data we want to make available to our component tree.
function App() { const theme = "light"; return (
); }
So now the value of “light” is available to all our components (which we will write soon).
Creating Our Theme File
Now, we will create our theme file that will contain the different color values for both our light and dark themes. Create a file in your
/src
folder called
Colors.js
.
In
Colors.js
, we will create an object called
AppTheme
. This object will contain the colors for our themes. Once you are done, export the
AppTheme
object like so:
const AppTheme = { light: { textColor: "#000", backgroundColor: "#fff" }, dark: { textColor: "#fff", backgroundColor: "#333" } } export default AppTheme;
Now it’s time to start creating our different React components.
Creating Our React Components
Let’s create the following components:
-
Header
-
ThemeToggler
-
MainWithClass
Header.jsx
import React from "react"; import ThemeToggler from "./ThemeToggler"; const headerStyles = { padding: "1rem", display: "flex", justifyContent: "space-between", alignItems: "center" } const Header = () => { return(
Context API
); } export default Header;
ThemeToggler.jsx
(For now, we will just return an empty
div
.)
import React from "react"; import ThemeContext from "../Context/ThemeContext"; const themeTogglerStyle = { cursor: "pointer" } const ThemeToggler = () => { return(
); } export default ThemeToggler;
Consuming Context With Class-Based Components
Here, we will use the value of our
ThemeContext
. As you may already know, we have
two methods of writing components in React
: through functions or classes. The process of use context in both methods is different so we will create two components to serve as the main section of our application:
MainWithClass
and
MainWithFunction
.
Let us start with
MainWithClass
.
MainWithClass.jsx
We will have to import our
ThemeContext
and
AppTheme
. Once that is done, we will write a class that returns our JSX from a render method. Now we have to consume our context. There are two methods to do this with class-based components:
-
The first method is through
Class.contextType
.
To use this method, we assign the context object from ourThemeContext
tocontextType
property of our class. After that, we will be able to access the context value usingthis.context
. You can also reference this in any of the lifecycle methods and even the render method.
import React, { Component } from "react"; import ThemeContext from "../Context/ThemeContext"; import AppTheme from "../Colors"; class Main extends Component{ constructor(){ super(); } static contextType = ThemeContext; render(){ const currentTheme = AppTheme[this.context]; return( ); } }
After assigningThemeContext
to thecontextType
property of our class, I saved the current theme object in thecurrentTheme
variable.
Now, we will grab the colors from thecurrentTheme
variable and use them to style some markup.
render() { const currentTheme = AppTheme[this.context]; return (
Heading 1
This is a paragraph
That’s it! This method, however, limits you to consuming only one context. -
The second method is
ThemeContext.Consumer
that involves the use of a Consumer. Each context object also comes with a Consumer React component which can be used in a class-based component. The consumer component takes a child as a function and that function returns a React node. The current context value is passed to that function as an argument.
Now, let us replace the code in ourMainWithClass
component with this:
class Main extends Component { constructor() { super(); this.state = { } } render(){ return(
{ (theme) => { const currentTheme = AppTheme[theme]; return( ); } }Heading 1
This is a paragraph
As you can see, we used the current value of ourThemeContext
which we aliased as “theme” and we grabbed the color values for that theme mode and assigned it to the variablecurrentTheme
. With this method, you can use multiple Consumers.
Those are the two methods of consuming context with class-based components.
Consuming Context With Functional Components
Consuming context with functional components is easier and less tedious than doing so with class-based components. To consume context in a functional component, we will use a hook called
useContext
.
Here is what consuming our
ThemeContext
with a functional component would look like:
const Main = () => { const theme = useContext(ThemeContext); const currentTheme = AppTheme[theme]; return(
Heading 1
This is a paragraph
); } export default Main;
As you can see, all we had to do was use our
useContext
hook with our
ThemeContext
passed in as an argument.
Note : You have to use these different components in the App.js file in order to see the results.
Updating Our Theme With The
ThemeToggler
Component
Now we are going to work on our
ThemeToggler
component. We need to be able to switch between the light and dark themes. To do this, we are going to need to edit our
ThemeContext.js
. Our
React.createContext
will now take an object resembling the result of a
useState
hook as an argument.
const ThemeContext = React.createContext(["light", () => {}]);
We passed an array to the
React.createContext
function. The first element in the array is the current theme mode and the second element is the function that would be used to update the theme. As I said, this just resembles the result of a
useState
hook but it is not exactly the result of a
useState
hook.
Now we will edit our
App.js
file. We need to change the value passed to the provider to a
useState
hook. Now the value of our Theme Context is a
useState
hook whose default value is “light”.
function App() { const themeHook = useState("light"); return (
); }
Writing Our
ThemeToggler
Component
Let us now actually write our
ThemeToggler
component:
import React,{useContext} from "react"; import ThemeContext from "../Context/ThemeContext"; const themeTogglerStyle = { cursor: "pointer" } const ThemeToggler = () => { const[themeMode, setThemeMode] = useContext(ThemeContext); return(
{setThemeMode(themeMode === "light"? "dark": "light")}}>
{themeMode === "light" ? "🌙" : "☀️"}
); } export default ThemeToggler;
Since the value of our theme context is now a hook whenever we call
useContext
on it, it will return an array. Using destructuring, we were able to grab the elements from the array. We then wrote an
onClick
event handler for our
ThemeToggler
. With that code, whenever the theme toggler is clicked, it will switch the theme of our application.
Now we will edit the different versions of our
Main
component.
Editing Our
MainWithClass
Component
-
The version of the
MainWithClass
component that uses theClass.contextType
method:
import React, { Component } from "react"; import ThemeContext from "../Context/ThemeContext"; import AppTheme from "../Colors"; class Main extends Component{ constructor(){ super(); } static contextType = ThemeContext; render(){ const currentTheme = AppTheme[this.context[0]]; return(
Heading 1
This is a paragraph
-
The version of the
MainWithClass
component that uses theThemeContext.Consumer
method:
import React, { Component } from "react"; import ThemeContext from "../Context/ThemeContext"; import AppTheme from "../Colors"; class Main extends Component { constructor() { super(); this.state = {} } render() { return (
{ ([theme]) => { const currentTheme = AppTheme[theme]; return( ); } } export default Main;Heading 1
This is a paragraph
Editing Our
MainWithFunction
Component
The
MainWithFunction
Component should be edited as the following:
import React, { useContext } from "react"; import ThemeContext from "../Context/ThemeContext"; import AppTheme from "../Colors"; const Main = () => { const theme = useContext(ThemeContext)[0]; const currentTheme = AppTheme[theme]; return(
Heading 1
This is a paragraph
); } export default Main;
Conclusion
That’s it! We have succeeded in implementing two theme modes for our React app using the Context API.
In the process, we have learned:
- What the Context API is and the problem it solves;
- When to use the Context API;
-
Creating
Context
and consuming it in both functional and class-based components.
Further Reading on SmashingMag:
- Styling In Modern Web Apps
- Building Mobile Apps With Ionic And React
- Build A PWA With Webpack And Workbox
- Getting To Know The MutationObserver API