Mastering React Design Patterns: From Containers to Custom Hooks
This article reviews essential React design patterns—including container/presentational components, higher‑order components, render props, compound components, and custom hooks—explaining the underlying SOLID principles, providing clear code examples, and offering guidance on when to apply each pattern.
Design patterns are reusable solutions to common problems and are considered best practices for writing maintainable, robust code. This article reviews several React community design patterns and the design principles they follow.
Key Design Principles
Single Responsibility Principle : each class, function, or module should have only one responsibility.
Open‑Closed Principle : entities should be open for extension but closed for modification.
Dependency Inversion Principle : depend on abstractions rather than concrete implementations.
Don’t Repeat Yourself (DRY) : avoid duplicated code to simplify maintenance.
Composition over Inheritance : prefer composing objects to inheriting from them.
React Design Patterns
Container & Presentational Components
Separate UI rendering (presentational) from data fetching and event handling (container). This respects the Single Responsibility Principle and DRY.
Example
import React from "react";
// Presentational component
export default function ImageList({ images, onClick }) {
return images.map((img, i) => <img src={img} key={i} onClick={onClick} />);
}
// Container component
export default class ImageListContainer extends React.Component {
constructor() {
super();
this.state = { images: [] };
}
componentDidMount() {
fetch("https://images.com")
.then(res => res.json())
.then(({ images }) => this.setState({ images }));
}
handleClick() {
// ...
}
render() {
return <ImageList images={this.state.images} onClick={this.handleClick} />;
}
}Higher‑Order Component (HOC)
A function that takes a component and returns a new component, enabling logic reuse. Used by Redux’s connect and Relay’s createFragmentContainer.
Principles
DRY: encapsulate reusable logic in the HOC.
Composition over Inheritance: HOCs can be nested.
Example
import React from "react";
export default function withLoader(Component, url) {
return class HOC extends React.Component {
constructor(props) {
super(props);
this.state = { loading: true, data: {} };
}
componentDidMount() {
fetch(url)
.then(res => res.json())
.then(({ data }) => this.setState({ data }))
.finally(() => this.setState({ loading: false }));
}
render() {
if (this.state.loading) {
return <div>Loading...</div>;
}
return <Component {...this.props} data={this.state.data} />;
}
};
}Render Prop
Components expose a function prop that allows callers to customize rendering logic. Libraries such as React Router, Downshift, and Formik use this pattern.
Principles
DRY: move reusable rendering logic into the component.
Dependency Inversion: inject rendering behavior via the prop.
Open‑Closed: the component provides extension points without modification.
Example
import React from "react";
class Loader extends React.Component {
constructor(props) {
super(props);
this.state = { loading: true, data: {} };
}
componentDidMount() {
fetch(url)
.then(res => res.json())
.then(({ data }) => this.setState({ data }))
.finally(() => this.setState({ loading: false }));
}
render() {
if (this.state.loading) {
return <div>Loading...</div>;
}
return this.props.renderData(this.state.data);
}
}Compound Components
Multiple components work together through shared state or context to accomplish a task, e.g., Select and Select.Option. Libraries like Semantic UI use this pattern.
Principles
Single Responsibility: each sub‑component handles its own concern.
Open‑Closed: new sub‑components can extend functionality without altering existing ones.
Example
import React from "react";
const SelectContext = React.createContext({});
export function Select({ value, onChange, children }) {
const [open, setOpen] = React.useState(false);
const [val, setValue] = React.useState(value);
return (
<div className="select">
<div className="select-value" onClick={() => setOpen(true)}>{val}</div>
<SelectContext.Provider value={{ value: val, setOpen, setValue: newValue => { setValue(newValue); if (value !== newValue) onChange(newValue); } }}>
{open && children}
</SelectContext.Provider>
</div>
);
}
function Option({ children, value }) {
const { setOpen, setValue, value: selectedValue } = React.useContext(SelectContext);
return (
<div className={`select-option ${value === selectedValue ? "selected" : ""}`} onClick={() => { setValue(value); setOpen(false); }}>
{children}
</div>
);
}
function OptionGroup({ children, label }) {
return (
<div className="select-option-group">
<div className="select-option-group-label">{label}</div>
{children}
</div>
);
}
Select.Option = Option;
Select.OptionGroup = OptionGroup;Custom Hooks
Custom hooks encapsulate reusable stateful logic, offering a finer‑grained alternative to component‑based patterns.
Principles
DRY: place reusable logic in a hook.
Single Responsibility: each hook focuses on a single concern.
Example
import { useState, useEffect } from "react";
function useLoader(url) {
const [data, setData] = useState({});
const [loading, setLoading] = useState(false);
useEffect(() => {
setLoading(true);
fetch(url)
.then(res => res.json())
.then(({ data }) => setData({ data }))
.finally(() => setLoading(false));
}, [url]);
return { data, loading };
}Conclusion
The design patterns discussed follow SOLID principles and help developers write robust, maintainable code, but they should be applied judiciously; the YAGNI principle reminds us to avoid unnecessary complexity.
References
https://reactjs.org/docs/composition-vs-inheritance.html
https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0
https://github.com/reduxjs/react-redux/blob/master/docs/api/connect.md#connect
https://relay.dev/docs/v10.1.3/fragment-container/#createfragmentcontainer
https://reactjs.org/docs/composition-vs-inheritance.html
https://reacttraining.com/react-router/web/api/Route/render-func
https://github.com/paypal/downshift
https://github.com/jaredpalmer/formik
https://react.semantic-ui.com/
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Alibaba Cloud Developer
Alibaba's official tech channel, featuring all of its technology innovations.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
