React Design Pattern Series: Mastering Render Props Pattern

Introduction

React render props have become a powerful and flexible design pattern in the world of React development. They offer a way to share code between components and create highly reusable and customizable UI components. In this article, we'll explore the fundamental concept behind render props, understand why they are beneficial, and dive into real-world use cases to showcase their practical applications. We'll exclusively use functional components to keep things simple and accessible.

Understanding render props

At its core, a render prop is a function passed as a prop to a component, allowing that component to render content controlled by the parent. This enables the parent component to delegate the rendering logic to the child component while maintaining control over the data and behavior.

Here's a simple example to illustrate the basic concept.

File: App.js

import RenderPropComponent from './RenderPropComponent';
const App = () => {
  return (
    <div>
      <h1>Parent Component</h1>

      <RenderPropComponent 
        render={(data) => <p>Data from parent: {data}</p>} 
      />
     <RenderPropComponent
        render={(data) => <h1>Data from parent: {data}</h1>}
      />
    </div>
  );
};
export default App;

File: RenderPropComponent.js

const RenderPropComponent = ({ render }) => {
  const data = "Hello from RenderPropComponent!";
  return render(data);
};

export default RenderPropComponent;

Parent Component

In this example, the RenderPropComponent receives a render prop, which is a function. The parent component defines the rendering logic and passes it to the child. This way, the child can utilize the parent's logic while keeping the data flow controlled by the parent.

Why do we use render props?

  1. Reusability: Render props promote reusability by allowing components to encapsulate behavior and rendering logic separately. This separation makes it easy to reuse the same functionality across various components without duplicating code.
  2. Flexibility: Render props offer flexibility in component composition. Since the parent component controls the rendering logic, it can customize the child component's appearance and behavior dynamically.
  3. Improved testing: By separating concerns, render props make it easier to test individual components. You can test the rendering logic and behavior of the child component independently, ensuring a more straightforward and focused testing process.

Real-world use cases. Toggle component

Imagine a toggle component that can be used to show/hide content. The render prop approach allows us to customize the UI based on the state of the toggle

File: ToggleComponent.js

import React, { useState } from 'react';

const ToggleComponent = ({ render }) => {
  const [isToggled, setToggled] = useState(false);

  const toggle = () => setToggled(!isToggled);

  return render({ isToggled, toggle });
};

export default ToggleComponent;

Now, you can use this ToggleComponent in various scenarios, controlling the rendering logic from the parent.

File: App.js

import React from 'react';
import ToggleComponent from './ToggleComponent';
const App = () => {
  return (
    <div>
      <ToggleComponent
        render={({ isToggled, toggle }) => (
          <div>
            <button onClick={toggle}>Toggle</button>
            {isToggled && <h1>Content to toggle</h1>}
          </div>
        )}
      />
    </div>
  );
};
export default App;

Conclusion

In mastering React render props, we've discovered the fundamental concept, the 'why' behind using this design pattern, and its benefits. By exploring real-world use cases, we've seen how render props provide reusability and flexibility, making our components more powerful and adaptable. Incorporate this pattern into your React projects, and you'll find yourself creating components that are not only efficient but also highly customizable. Happy coding!