Skip to content

Commit 04cbc67

Browse files
committed
Using the Context API React.createContext()
1 parent be3f3a3 commit 04cbc67

6 files changed

Lines changed: 110 additions & 46 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,3 +103,4 @@ In this repository, I've implemented (or implementing) all the concepts related
103103
3. `Refs` in functional components using `useRef()` hook: [Commit Details](https://github.com/Ch-sriram/react/commit/9088c3ac9007c341cbc85b816ac57ec500ad6afa)
104104
14. Context API in React
105105
1. Problems with `prop` chaining — Why use the concept of Context? [Commit Details](https://github.com/Ch-sriram/react/commit/be6a373389a4942902f261a988f46aefb5601122)
106+
2. Using the Context API using `React.createContext()`: [Commit Details]()

react-examples/src/components/Cockpit/Cockpit.component.jsx

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import React, { useEffect, useRef } from 'react';
22
import CockpitStyleClasses from './Cockpit-style.module.css';
3+
import AuthContext from '../../context/auth/auth.context';
34

45
const Cockpit = props => {
56
const togglePersonsBtnRef = useRef(null);
@@ -27,6 +28,13 @@ const Cockpit = props => {
2728
classes.push(CockpitStyleClasses.bold);
2829
}
2930

31+
/**
32+
* We will wrap the required button inside the
33+
* <AuthContext/> context component where we return the
34+
* button by passing in the function with the `context`
35+
* variable.
36+
*/
37+
3038
return (
3139
<div className={CockpitStyleClasses.Cockpit}>
3240
<h1>{props.title}</h1>
@@ -39,9 +47,16 @@ const Cockpit = props => {
3947
>
4048
Toggle Persons
4149
</button>
42-
<button onClick={props.login}>{"Log In/Out"}</button>
50+
<AuthContext.Consumer>
51+
{(context) => <button onClick={context.login}>{"Log In/Out"}</button>}
52+
</AuthContext.Consumer>
4353
</div>
4454
);
4555
};
4656

4757
export default React.memo(Cockpit);
58+
59+
/**
60+
* Because of this, we don't need to send the `loginHandler()`
61+
* as a prop to the <Cockpit/> component.
62+
*/

react-examples/src/components/Persons/Person/Person.component.jsx

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import PropTypes from 'prop-types';
66
import PersonStyleClasses from './Person-style.module.css';
77
import Aux from '../../../hoc/Auxiliary/Auxiliary.hoc';
88
import withClass from '../../../hoc/WithClass/withClass.closureHOC';
9+
import AuthContext from '../../../context/auth/auth.context';
910

1011
class Person extends PureComponent {
1112
inputElementRef = React.createRef();
@@ -14,19 +15,28 @@ class Person extends PureComponent {
1415
this.inputElementRef.current.focus();
1516
}
1617

18+
/**
19+
* Wherever we need to use the context object, we have to
20+
* always consume it, so we'll consume it here as shown below.
21+
*
22+
* Here, we can see that the content wrapped inside
23+
* <AuthContext.Consumer/> is executing a function
24+
* which takes in an argument, which is the `context`
25+
* variable, through which, we can directly access the
26+
* context variable or the context function.
27+
*/
28+
1729
render() {
1830
console.log("[Person.jsx] rendering...");
1931
return (
2032
<Aux>
21-
{
22-
this.props.isAuth ?
23-
<p>Authenticated</p> :
24-
<p>Please Log In</p>
25-
}
33+
<AuthContext.Consumer>
34+
{context => context.authenticated ? <p>Authenticated</p> : <p>Please Log In</p>}
35+
</AuthContext.Consumer>
2636
<p onClick={this.props.click}>
2737
I'm {this.props.name} and I'm {this.props.age} years old!
2838
</p>
29-
<input
39+
<input
3040
ref={this.inputElementRef}
3141
type="text"
3242
onChange={this.props.changed}

react-examples/src/components/Persons/Persons.component.jsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,16 @@ class Persons extends PureComponent {
1818
console.log("[Persons.js] componentWillUnmount");
1919
}
2020

21+
/**
22+
* We need not consume the context that we wrapped the
23+
* <Persons/> component with, in here. We can directly
24+
* consume it inside the <Person/> component.
25+
*
26+
* And also, we don't need to send in the `isAuth` prop
27+
* to the <Person/> component, and also, we need not take
28+
* in `isAuthenticated` prop from the <App/> component.
29+
*/
30+
2131
render() {
2232
console.log("[Persons.jsx] rendering...");
2333
return this.props.persons.map((person, index) => {
@@ -28,7 +38,6 @@ class Persons extends PureComponent {
2838
name={person.name}
2939
age={person.age}
3040
changed={(event) => this.props.nameChanged(event, person.id)}
31-
isAuth={this.props.isAuthenticated}
3241
/>
3342
);
3443
});

react-examples/src/containers/App.js

Lines changed: 51 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -7,42 +7,11 @@ import Cockpit from '../components/Cockpit/Cockpit.component';
77
// import WithClass from '../hoc/WithClass/withClass.hoc';
88
import withClass from '../hoc/WithClass/withClass.closureHOC';
99
import Aux from '../hoc/Auxiliary/Auxiliary.hoc';
10+
import AuthContext from '../context/auth/auth.context';
1011

1112
// Required Styles
1213
import AppStyleClasses from './App.module.css';
1314

14-
/**
15-
* Assume that, for the <Person/> component, we want to add
16-
* in a dummy authentication whose handler is written inside
17-
* the <App/> component, and it is invoked from the <Cockpit/>
18-
* component. The <Cockpit/> component receives the
19-
* loginHandler() function's reference as a `prop` from App
20-
* component.
21-
*
22-
* Now, the loginHandler() event handler, which is invoked from
23-
* the <Cockpit/> component, will simply change some state
24-
* object, let's say we have a new state variable called
25-
* `authenticated`, which is initially false.
26-
*
27-
* Inside the loginHandler() function, we can call setState and
28-
* set `authenticated` to true, and this loginHandler()'s
29-
* reference is sent to the <Cockpit/> component as a property
30-
* called `login`.
31-
*
32-
* Now, we also want to get the information whether every
33-
* <Person/> component is authenticated or not. And since the
34-
* state of the authentication status, which is `authenticated`
35-
* is stored at <App/> component, and also, since the App
36-
* component only has access to the <Persons/> component, we
37-
* can send in the state value of `authenticated` as a property
38-
* to the <Persons/> component (let's say `isAuthenticated`
39-
* prop). Inside the <Persons/> component, we can pass the
40-
* `isAuthenticated` property to each <Person/> as (say)
41-
* `isAuth` property. And inside the <Person/> component, we
42-
* can print information on each <Person/> whether it is
43-
* "Authenticated" or "Not Authenticated".
44-
*/
45-
4615
class App extends Component {
4716
constructor(props) {
4817
super(props);
@@ -115,17 +84,49 @@ class App extends Component {
11584
console.log("[App.js] rendering...");
11685
let persons = null;
11786

87+
/**
88+
* Because of context object, we need not send information
89+
* on `state.authenticated` as a prop.
90+
*/
91+
11892
if (this.state.showPersons) {
11993
persons = (
12094
<Persons
12195
persons={this.state.persons}
12296
clicked={this.deletePersonHandler}
12397
nameChanged={this.nameChangedHandler}
124-
isAuthenticated={this.state.authenticated}
12598
/>
12699
);
127100
}
128101

102+
/**
103+
* Now, the imported <AuthContext/> component should wrap
104+
* all the components that would need to use the context
105+
* variables/functions in our app.
106+
*
107+
* In this case, the <Person/> and the <Cockpit/>
108+
* components need to use the context variable/functions,
109+
* and so, we'll wrap that part of the returned JSX from
110+
* this App component, inside <AuthContext.Provider/>
111+
* component. Because we're providing the context to the
112+
* components that are wrapped inside the <AuthContext/>
113+
* component.
114+
*
115+
* The <AuthContext.Provider/> component, takes in a
116+
* `value` prop, in which we pass in the new values to the
117+
* context, depending on what we're trying to provide the
118+
* context for. If the `value` prop isn't described, then
119+
* the default context's definition will be taken as the
120+
* value of the context.
121+
*
122+
* In this case, both <Cockpit/> and the <Persons/>
123+
* components can access the context object's variables or
124+
* functions. In case of <Persons/> component, since
125+
* <Person/> is the child component of <Persons/>
126+
* component, we can also use the context directly inside
127+
* the <Person/> component, which is what we want.
128+
*/
129+
129130
return (
130131
<Aux>
131132
<button
@@ -137,20 +138,32 @@ class App extends Component {
137138
>
138139
{this.state.showCockpit ? "Remove Cockpit" : "Show Cockpit"}
139140
</button>
140-
{
141-
this.state.showCockpit ?
141+
<AuthContext.Provider value={{
142+
authenticated: this.state.authenticated,
143+
login: this.loginHandler
144+
}}>
145+
{this.state.showCockpit ? (
142146
<Cockpit
143147
title={this.props.title}
144148
showPersons={this.state.showPersons}
145149
personsLength={this.state.persons.length}
146150
clicked={this.togglePersonsHandler}
147151
login={this.loginHandler}
148-
/> : null
149-
}
150-
{persons}
152+
/>
153+
) : null}
154+
{persons}
155+
</AuthContext.Provider>
151156
</Aux>
152157
);
153158
}
154159
}
155160

156161
export default withClass(App, AppStyleClasses.App);
162+
163+
/**
164+
* The one thing that doesn't change when using the Context API
165+
* is that, we still maintain the info to be passed, inside the
166+
* state of the component, because, react will only re-render
167+
* when the state/props in some component down the component
168+
* tree changes.
169+
*/
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import React from 'react';
2+
3+
/**
4+
* `React.createContext()` allows us to initialize our
5+
* respective context with a default value, because the context
6+
* object in the end, is just a globally available object,
7+
* which we decide whether to use, or not. Now, this context
8+
* object can be passed between react components w/o using the
9+
* `props` and therefore now, there will be no prop-chaining.
10+
*/
11+
const authContext = React.createContext({
12+
authenticate: false,
13+
login: () => {}
14+
});
15+
16+
export default authContext;

0 commit comments

Comments
 (0)