A component with a render prop takes a function that returns a React element and calls it instead of implementing its own render logic.
If a situation arises where we have to repeate the same logic mutilple times, We will have to Copy and paste a lot of code.
Render Props helps us minimise this and helps us to utilise the already written code by letting us pass the logic as a function through props to other components.
Another situation where we can use Render Props is, when the use Higher Order Components becomes very complicated.
The Implementation of Render Props in React can be very confusing at first but becomes easy later.
So ,To better understand the concept, we will be doing a task
So, The first button will have the following code :-
import React from "react";
class Button1 extends React.Component {
constructor(props){
super(props)
this.state = {
counter: 0
}
}
HandleCounter = () => {
this.setState({ counter: this.state.counter + 1 });
}
render() {
return (
<button onClick={() => {
this.HandleCounter();
}}>Click Counter : {this.state.counter}</button>
)
}
}
export default Button1;
And, The code for second button will be
import React from "react";
class Button2 extends React.Component {
constructor(props){
super(props)
this.state = {
counter: 0
}
}
HandleCounter = () => {
this.setState({ counter: this.state.counter + 1 });
}
render() {
return (
<button onMouseOver={() => {
this.HandleCounter();
}}>Click Counter : {this.state.counter}</button>
)
}
}
export default Button2;
The Output of the above Two Components is :-
The above Code works fine. But there is one problem of repeating our logic inside both components
constructor(props){
super(props)
this.state = {
counter: 0
}
}
HandleCounter = () => {
this.setState({ counter: this.state.counter + 1 });
}
This Block of Code is Exactly the Same for both components, And although this might not represent much But we can avoid this using render props.
The idea behind using Render Props is to create a Wrapper Class.
This Wrapper class has its state components just like above elements but unlike above elements, it does not render any component directly
Instead, It calls a Function which , in turn renders the required components.
The theory might be a little confusing so lets just start with the code
import React from 'react';
class Wrapper extends React.Component {
render() {
}
}
export default Wrapper;
import React from 'react';
class Wrapper extends React.Component {
constructor(props) {
super(props)
this.state = {
counter: 0
}
}
HandleCounter = () => {
this.setState({ counter: this.state.counter + 1 });
}
render() {
}
}
export default Wrapper;
import Button1 from "./Button1"
import Button2 from "./Button2"
import Wrapper from './Wrapper'
function App() {
return (
<div>
</div>
);
}
export default App;
Now, The Next step to use render props method is a little complicated to understand at first but becomes really easy later.
We need to render the Wrapper component inside the app.js file and according to the defination of render props, We need to pass a function as a prop to the component.
We will do exactly that. We will pass a function that recieves the counter state and the handleCounter function and returns our button component.
Like so :-
import Button1 from "./Button1"
import Button2 from "./Button2"
import Wrapper from './Wrapper'
function App() {
return (
<div>
<Wrapper render={(counter, handleCounter) => {
// passing a function as a prop by the name if render
// the function recieves the state counter variable and handleCounter function as arguements
return <Button1 counter={counter} handleCounter={handleCounter} />
// and then we pass the recieved arguements as props to the component we are trying to render so that the child
component will have access to it
}} />
</div>
);
}
export default App;
Now We will go to our Wrapper File and call this function from there and pass the state and handleCounter function as parameters.
import React from 'react';
class Wrapper extends React.Component {
constructor(props) {
super(props)
this.state = {
counter: 0
}
}
HandleCounter = () => {
this.setState({ counter: this.state.counter + 1 });
}
render() {
return (
//passing arguements to the function so we will have access to them.
this.props.render(this.state.counter, this.HandleCounter)
)
}
}
export default Wrapper;
Now that we have moved all the logic from our button components to the wrapper component, we will refactor it a little so our code can work again,
import React from "react";
class Button1 extends React.Component {
render() {
return (
<button onClick={() => {
this.props.handleCounter();
//instead of this.handleCounter we will now use the above syntax as now , this function is given to us as a prop from Wrapper class and earlier it used to be a local function
}}>Click Counter : {this.props.counter}</button>
// now we will use this.props.counter instead of this.state.counter as now the value is the state value of Wrapper classand is passed to us as props and not this class.
)
}
}
export default Button1;
Output of this code will be :-
;
Now , We Can do the same for Button2 Class.
import React from "react";
class Button2 extends React.Component {
render() {
return (
<button onMouseOver={() => {
this.props.HandleCounter();
}}>Hover Counter : {this.props.counter}</button>
)
}
}
export default Button2;
And we will add another Wrapper Element to the app.js file like so :-
import Button1 from "./Button1"
import Button2 from "./Button2"
import Wrapper from './Wrapper'
function App() {
return (
<div>
// "render" prop can be named anything. But the general practise is to name it "render" only when we are using render props technique
<Wrapper render={(counter, handleCounter) => {
return <Button1 counter={counter} handleCounter={handleCounter} />
}} />
<Wrapper render={(counter, handleCounter) => {
return <Button2 counter={counter} handleCounter={handleCounter} />
}} />
</div>
);
}
export default App;
And our Code works again :-
First we Created the function in app.js file while calling the Wrapper Component which had the refference of state variable and the updation function of Wrapper Component.
Then inside the above function, we returned our button element and passed the state variable and the updation function of its parent class to it.
Then we called that render function inside the Wrapper Component as it was passes to it as prop.
Then we got our button element and the flow is :-