Introduction
In the
previous article, we have learned about Higher-Order Components in React along with its usage. In this article, we will go through Render Props and the concept of Context in React.
Render Props
Render Props in React is something where a component’s prop is assigned a function and that is called in the render method of the component. Calling the function will return a React element or component. Third-party libraries like React Router and Downshift uses this approach.
A render prop is a function prop that the component uses to know what to render. Through this, we can also pass a state to another.
Let's see a demo.
Create a Movement.js file and code as below,
- import React, { Component } from 'react'
-
- export class Movement extends Component {
-
- constructor(props) {
- super(props)
-
- this.state = {
- x:0,
- y:0
- }
- }
-
- onMouseMove =(event) => {
- this.setState({
- x:event.clientX,
- y:event.clientY
- })
- }
-
- render() {
- const{x,y} = this.state
- return (
- <div style={{height : '100%'}} onMouseMove={this.onMouseMove}>
- <h1> The current mouse position is ({x},{y})</h1>
- </div>
- )
- }
- }
-
- export default Movement
Now add this Movement.js in App.js,
- import React from 'react';
- import './App.css';
- import './css/custom.css';
- import Movement from './components/Movement'
-
- function App() {
- return (
- <div>
- <Movement></Movement>
- </div>
- );
- }
-
- export default App;
The above code will display output as below.
Now let’s go further with the demo, we will create a component named Monkey in which on moving the mouse, the monkey will also move following the banana.
Let’s see the demo. Change the code of Movement.js as below.
- import React, { Component } from 'react'
- import PropTypes from 'prop-types'
-
- export class Movement extends Component {
-
- static propTypes = {
- render: PropTypes.func.isRequired
- }
-
- state={x:0,y:0}
-
- onMouseMove =(event) => {
- this.setState({
- x:event.clientX,
- y:event.clientY
- })
- }
-
- render() {
- return (
- <div onMouseMove={this.onMouseMove}>
- {this.props.render(this.state)}
- </div>
- )
- }
- }
-
- export default Movement
Now, create Monkey.js with the below code. For below, we also need 2 images of monkey and banana respectively which are uploaded in the source code of Demo2.
- import React, { Component } from 'react'
- import monkey from '../images/monkey.jpg'
- import banana from '../images/banana.jpg'
-
- export class Monkey extends Component {
- render() {
- const{x,y} = this.props.Movement
-
- return (
- <div>
- <img src={monkey} alt='monkey' style={{position:'relative',left:x,right:y,height:150}}></img>
- <img src={banana} alt='banana' style={{position:'relative',left:(x+10),right:(y+10),height:30}}></img>
- </div>
- )
- }
- }
-
- export default Monkey
Now, in the final step, call Monkey.js in App.js.
- import React from 'react';
- import './App.css';
- import './css/custom.css';
- import Movement from './components/Movement'
- import Monkey from './components/Monkey'
-
- function App() {
- return (
- <div>
- <Movement render={({x,y}) => (
- <Monkey Movement={{x,y}}/>
- )}/>
-
- </div>
- );
- }
-
- export default App;
This will display the output as below.
In the above image, on moving the mouse, the monkey will also move. The above demo is quite interesting and lets us allow to animate other animation can be created using react-motion API.
Now, we will see the concept of Context in ReactJS.
Context
Context in React.js is the concept to pass data through a component tree without passing props down manually to each level.
In basic React application, to pass data from parent to child is done using props but this is a heavy approach if data needs to be passed on multiple levels like passing username or theme required by most of the components in the application. Context lets you provide the functionality to share values among components without explicitly passing props through every level of the component tree.
Mainly context is used when the current authentication, themes, or preferred language need to be passed to the child levels when not required to pass props.
To get data using context we need to follow 3 steps,
- We need to first create the context.
- Then we need to provide value to the context.
- Lastly, we need to consume the value of context.
Now let’s have a look at the demo,
For Example - In the current demo, we will see how to use username in child component using context.
First, we will create a new file namely UserContext.js. This is the first step where we are creating context
- import React from 'react'
-
- const UserContext = React.createContext()
-
- const UserProvider = UserContext.Provider
-
- const UserConsumer = UserContext.Consumer
-
- export {UserProvider,UserConsumer}
Now, we will create a nested structure in which I will create 3 level components Master Component -> Home Component -> Profile Component.
App.js in which we will execute second step we need to provide value to consumer,
- import React from 'react';
- import './App.css';
- import './css/custom.css';
- import MasterComponent from './components/MasterComponent'
- import { UserProvider } from './components/UserContext';
-
- function App() {
- return (
- <div className="App">
- <UserProvider value="New User">
- <MasterComponent />
- </UserProvider>
- </div>
- );
- }
-
- export default App;
MasterComponent.js - import React, { Component } from 'react'
- import HomeComponent from './HomeComponent'
- class MasterComponent extends Component {
- render() {
- return (
- <div>
- <HomeComponent/>
- </div>
- )
- }
- }
-
- export default MasterComponent
Now second level component is named HomeComponent.js
- import React, { Component } from 'react'
- import ProfileComponent from './ProfileComponent'
-
- class HomeComponent extends Component {
- render() {
- return (
- <div>
- <ProfileComponent/>
- </div>
- )
- }
- }
-
- export default HomeComponent
And third level Component named ProfileComponent in which we are going to consume context value
- import React, { Component } from 'react'
- import { UserConsumer } from './UserContext';
-
- class ProfileComponent extends Component {
- render() {
- return (
- <UserConsumer>
- {
- (username) => {
- return <div>Hello {username}</div>
- }
- }
- </UserConsumer>
- )
- }
- }
-
- export default ProfileComponent
Now the output will be displayed as below,
If in UserContext.js while creating context we will define default value and if in case we don't provide any value in component then it will take default value from UserContext,
- import React from 'react'
- const UserContext = React.createContext('Guest')
- const UserProvider = UserContext.Provider
- const UserConsumer = UserContext.Consumer
- export {UserProvider,UserConsumer}
And App.js code will be as below,
- import React from 'react';
- import './App.css';
- import './css/custom.css';
- import MasterComponent from './components/MasterComponent'
- import { UserProvider } from './components/UserContext';
-
- function App() {
- return (
- <div className="App">
- {}
- <MasterComponent />
- {}
- </div>
- );
- }
-
- export default App;
The output will be as follows,
While using context we can only pass one value, for multiple values we need to use multiple context.
There is one more way to access context value using Context Type property.
First we need to export UserContext from UserContext.js file,
- import React from 'react'
- const UserContext = React.createContext('Guest')
- const UserProvider = UserContext.Provider
- const UserConsumer = UserContext.Consumer
- export {UserProvider,UserConsumer}
- export default UserContext
Now we will consume context value using contextType in HomeComponent.js
- import React, { Component } from 'react'
- import ProfileComponent from './ProfileComponent'
- import UserContext from './UserContext'
-
- class HomeComponent extends Component {
- render() {
- return (
- <div>
- Home Component {this.contextType}
- <ProfileComponent/>
- </div>
- )
- }
- }
-
- HomeComponent.contextType = UserContext
-
- export default HomeComponent
The output will be as below,
Accessing multiple context values,
- import React from 'react'
- const UserContext = React.createContext('Guest')
- const ThemeContext = React.createContext('theme')
- const UserProvider = UserContext.Provider
- const UserConsumer = UserContext.Consumer
- const ThemeProvider = ThemeContext.Provider
- const ThemeConsumer = ThemeContext.Consumer
- export {UserProvider,UserConsumer,ThemeConsumer,ThemeProvider}
- export default UserContext
and using it in App.js,
- import React from 'react';
- import './App.css';
- import './css/custom.css';
- import MasterComponent from './components/MasterComponent'
- import { UserProvider,ThemeProvider } from './components/UserContext';
-
- function App() {
- return (
- <div className="App">
- <UserProvider value="New User">
- <ThemeProvider value="red">
- <MasterComponent />
- </ThemeProvider>
- </UserProvider>
- </div>
- );
- }
-
- export default App;
Now consuming value of ThemeProvider in ProfileComponent.js file,
- import React, { Component } from 'react'
- import { UserConsumer,ThemeConsumer } from './UserContext';
-
- class ProfileComponent extends Component {
- render() {
- return (
- <UserConsumer>
- {
- username => (
- <ThemeConsumer>
- {color => (
- <div style={{ color: color }}>Hello {username}</div>
- )}
- </ThemeConsumer>
- )}
- </UserConsumer>
- )
- }
- }
-
- export default ProfileComponent
The output will be displayed as below,
Limitation of Context
- Context can only be used with the class component.
- Using context type only a single context value can be accessed. For accessing multiple values, we will need to use nested consumer context.
Summary
In this article, we have learned about the concept of Render Props and Context and its usage in React. You can download source code attached along with this article. Now in the next article, we will learn about some advanced concepts of HTTP and how Get and Post methods are used in React to fetch data from Server.