React has revolutionized frontend development with its component-based architecture. However, as applications grow, managing complexity becomes a challenge. Advanced React patterns help developers build scalable, maintainable, and efficient React applications.
HOCs are functions that take a component and return a new component with additional functionality. This pattern is useful for reusing logic such as authentication, logging, or theming.
import React from "react";
import { useAuth } from "../hooks/useAuth";
const withAuth = (Component: React.FC) => {
return (props: any) => {
const { isAuthenticated } = useAuth();
if (!isAuthenticated) {
return <p>Please log in to continue.</p>;
}
return <Component {...props} />;
};
};
export default withAuth;
Render Props allow components to share behavior using a function that returns UI elements.
const MouseTracker = ({ render }: { render: (position: { x: number, y: number }) => JSX.Element }) => {
const [position, setPosition] = React.useState({ x: 0, y: 0 });
React.useEffect(() => {
const handleMouseMove = (event: MouseEvent) => {
setPosition({ x: event.clientX, y: event.clientY });
};
window.addEventListener("mousemove", handleMouseMove);
return () => window.removeEventListener("mousemove", handleMouseMove);
}, []);
return render(position);
};
export default MouseTracker;
<MouseTracker render={({ x, y }) => <h2>Mouse Position: {x}, {y}</h2>} />
Compound components allow components to work together while keeping their logic encapsulated. This is useful for building flexible UI elements like tabs, modals, and dropdowns.
const Tabs = ({ children }: { children: React.ReactNode }) => {
const [activeIndex, setActiveIndex] = React.useState(0);
return (
<div>
{React.Children.map(children, (child, index) =>
React.cloneElement(child as React.ReactElement, {
activeIndex,
setActiveIndex,
})
)}
</div>
);
};
const Tab = ({ label, index, activeIndex, setActiveIndex }: any) => (
<button
className={activeIndex === index ? "bg-blue-500 text-white" : "bg-gray-200"}
onClick={() => setActiveIndex(index)}
>
{label}
</button>
);
export { Tabs, Tab };
<Tabs>
<Tab label="Tab 1" index={0} />
<Tab label="Tab 2" index={1} />
</Tabs>
React provides two ways to handle form inputs: controlled and uncontrolled components.
const ControlledInput = () => {
const [value, setValue] = React.useState("");
return (
<input
type="text"
value={value}
onChange={(e) => setValue(e.target.value)}
/>
);
};
const UncontrolledInput = () => {
const inputRef = React.useRef<HTMLInputElement>(null);
const handleSubmit = () => {
alert(inputRef.current?.value);
};
return (
<div>
<input type="text" ref={inputRef} />
<button onClick={handleSubmit}>Submit</button>
</div>
);
};
Context API and custom hooks help in state management without prop drilling.
const ThemeContext = React.createContext({ theme: "light", toggleTheme: () => {} });
const ThemeProvider = ({ children }: { children: React.ReactNode }) => {
const [theme, setTheme] = React.useState("light");
const toggleTheme = () => setTheme(theme === "light" ? "dark" : "light");
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
};
export { ThemeContext, ThemeProvider };
const ThemedComponent = () => {
const { theme, toggleTheme } = React.useContext(ThemeContext);
return (
<div className={theme === "light" ? "bg-white" : "bg-black text-white"}>
<button onClick={toggleTheme}>Toggle Theme</button>
</div>
);
};
Using these advanced React patterns will help you build scalable, reusable, and maintainable applications. Whether you're working with HOCs, Render Props, Compound Components, or Context, applying the right pattern at the right time is key to efficient development.
Happy coding! 🚀