Stop Coding before thinking Feat- Render Props and Compound Patterns Frontend System Design, Pt — 1
Table of contents
Write better code with these patterns
React Design patterns are a great way to way structure and share functionality in your applications.
Render Props patterns
The render props pattern is a technique that allows components to share functionality through props that are functions. By passing a function as a prop to a component, you enable that component to render content provided by the function. This pattern unlocks a world of possibilities for creating reusable and dynamic components.
Here’s an Example
// Counter component using Render Props
const Counter = ({ render }) => {
const [count, setCount] = useState(0);
const increment = () => {
setCount(count + 1);
};
const decrement = () => {
setCount(count - 1);
};
// Render the counter and control functions
return render(count, increment, decrement);
};
// Parent component using the Counter component
const App = () => {
return (
<div>
<h1>Render Props Counter</h1>
<Counter
render={(count, increment, decrement) => (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
</div>
)}
/>
</div>
);
};
The
Counter
component manages thecount
state using theuseState
hook which definesincrement
anddecrement
functions to update the count.The
Counter
component accepts arender
prop, which is a function that takescount
,increment
, anddecrement
as arguments. We then invoke therender
function and pass in thecount
,increment
, anddecrement
values.The parent
App
component renders theCounter
component and provides a rendering function as therender
prop.The rendering function within the parent component uses the provided count and functions to display the current count and buttons for incrementing and decrementing.
This simple example demonstrates how the Render Props pattern allows you to encapsulate functionality within a component and delegate the rendering and behavior control to the parent component.
But Manish how it is useful??
Let’s consider a scenario where you need to create two separate components that display a count with increment and decrement buttons. Instead of duplicating the count logic and rendering code in both components, you can use the Render Props pattern to encapsulate the count logic in a single Counter
component and share it between the two components.
const Counter = ({ render }) => {
const [count, setCount] = useState(0);
const increment = () => {
setCount(count + 1);
};
const decrement = () => {
setCount(count - 1);
};
// Render the counter and control functions
return render(count, increment, decrement);
};
// Component 1
const CounterDisplay1 = () => {
return (
<Counter
render={(count, increment, decrement) => (
<div>
<h2>Counter 1</h2>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
</div>
)}
/>
);
};
// Component 2
const CounterDisplay2 = () => {
return (
<Counter
render={(count, increment, decrement) => (
<div>
<h2>Counter 2</h2>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
</div>
)}
/>
);
};
// Parent component
const App = () => {
return (
<div>
<h1>Code Reusability with Render Props</h1>
<CounterDisplay1 />
<CounterDisplay2 />
</div>
);
};
Here you can see that both components can have an entirely different UI and other things but since the counter component is handling the logic part we can make use of the render prop pattern to share logic and reusability among different components.
React compound patterns
The React Compound Pattern is a design approach that involves composing components together to create a unified interface with shared behavior and state while allowing each individual component to remain relatively simple and focused on a specific aspect of the UI.
When to use Compound patterns ??
Complex UI Elements — Compound components are particularly valuable for creating complex UI elements that require coordination between multiple components. Examples include tabs, accordions, modals, sliders, and more.
Shared State and Behavior — When you need to share state or behavior across a set of components, the Compound Pattern provides a structured way to manage this shared functionality.
Simple examples are the select and options HTML elements. Both select and options HTML elements work in groups to provide the given functionality
// SelectMenu component
const SelectMenu = ({ options, onSelect }) => {
const [selectedOption, setSelectedOption] = useState(options[0]);
const handleSelectChange = (event) => {
const selectedValue = event.target.value;
setSelectedOption(selectedValue);
onSelect(selectedValue);
};
return (
<select className="select-menu" value={selectedOption} onChange={handleSelectChange}>
{options.map((option, index) => (
<option key={index} value={option}>
{option}
</option>
))}
</select>
);
};
// App component
const App = () => {
const themeOptions = ['Default', 'Dark', 'Light'];
const handleThemeChange = (selectedTheme) => {
// Logic to apply the selected theme
console.log(`Selected theme: ${selectedTheme}`);
};
return (
<div>
<h1>Select Option Menu Example</h1>
<SelectMenu options={themeOptions} onSelect={handleThemeChange} />
{/* Rest of the app content */}
</div>
);
};
This is an example of a theme switcher that is based on a compound pattern
The select menu component encapsulates the logic for rendering the select menu and handling selection changes.
The App component renders the Select Menu component and provides the available options as well as a callback function ( handle change ) to handle the selected option.
Conclusion
It’s not a hard and fast rule to always use these patterns feel free to write code as you like but using these will surely give you an edge on code quality, reusability, customization, and many more things.
Up Next
HOC pattern, Hooks pattern, and many more