React Advanced Patterns — Complete Guide to Production-Ready React
React advanced patterns help you catch errors, render outside the DOM tree, split code, and build abstractions that separate production apps from toy projects.
What You’ll Learn
- How to catch render errors with Error Boundaries so one crash doesn’t break the whole app
- How to render modals and tooltips outside the parent DOM tree using Portals
- How to split your bundle with lazy loading and Suspense
- How to reuse component logic with HOCs, Render Props, and forwardRef
- How React’s reconciliation algorithm works under the hood
Why React Advanced Patterns Matters
Simple React apps work fine with just components and hooks. But as your app grows — think the Durga Antivirus Pro dashboard with real-time threat maps, scan queues, and settings panels — you need patterns that handle errors without crashing the whole UI, lazy-load parts the user hasn’t visited yet, and render overlays that escape CSS overflow constraints.
Your Learning Path
flowchart LR
A[React Basics] --> B[React Hooks]
B --> C[React Advanced<br/>Patterns]
C --> D[Next.js<br/>Full-Stack React]
C --> E[React Native<br/>Mobile Apps]
style C fill:#3b82f6,color:#fff,stroke:#2563eb,stroke-width:3px
useState, useEffect, useRef), and JavaScript ES6+. Complete https://tutorials.dodatech.com/frameworks/react/react-hooks/ first.Error Boundaries
The problem: A JavaScript error in one component can crash the entire React app — the whole screen goes white with no way to recover.
The solution: Error Boundaries are React components that catch errors in their child component tree, log them, and display a fallback UI instead of crashing the whole app.
import { Component } from "react";
class ErrorBoundary extends Component {
constructor(props) {
super(props);
this.state = { hasError: false, error: null };
}
static getDerivedStateFromError(error) {
return { hasError: true, error };
}
componentDidCatch(error, info) {
console.error("Error caught:", error, info.componentStack);
}
render() {
if (this.state.hasError) {
return (
<div style={{ padding: 20, border: "1px solid red", borderRadius: 8 }}>
<h2>Something went wrong</h2>
<p>{this.state.error.message}</p>
<button onClick={() => this.setState({ hasError: false })}>
Try Again
</button>
</div>
);
}
return this.props.children;
}
}Line by line:
class ErrorBoundary extends Component— Error boundaries must be class components. There’s no hook equivalent yet in React 18.static getDerivedStateFromError(error)— Runs during the render phase. Receives the error and returns a state update (setshasError: true).componentDidCatch(error, info)— Runs during the commit phase. Use it for side effects like logging to Sentry or LogRocket.this.state.hasError— When true, renders the fallback UI instead of children.- The “Try Again” button resets
hasErrortofalse, which re-renders the children — effectively retrying.
setTimeout, fetch), or server-side rendering. For event handlers, use try/catch.Portals
The problem: Modals and tooltips need to appear above everything else. But if they’re nested inside a parent with overflow: hidden, they get clipped.
The solution: Portals let you render a child into a different DOM node — outside the parent’s DOM tree — while keeping it in the React component tree (so events and context still work).
import { createPortal } from "react-dom";
function Modal({ isOpen, onClose, children }) {
if (!isOpen) return null;
return createPortal(
<div style={{
position: "fixed", inset: 0, background: "rgba(0,0,0,0.5)",
display: "flex", alignItems: "center", justifyContent: "center"
}}>
<div style={{ background: "white", padding: 24, borderRadius: 8 }}>
{children}
<button onClick={onClose}>Close</button>
</div>
</div>,
document.body
);
}Line by line:
createPortal(jsx, domNode)— Takes JSX and a target DOM node. Here,document.body.- The modal renders as a child of
<body>, so it’s never clipped by parent CSS. - Despite rendering elsewhere, React context and event bubbling still work within the portal’s React tree.
if (!isOpen) return null;— Don’t render anything when the modal is closed.
Code Splitting & Suspense
The problem: Without code splitting, users download the entire app on first visit — even if they only see the login page.
The solution: Split your bundle into smaller “chunks” that load on demand:
import { lazy, Suspense } from "react";
const Dashboard = lazy(() => import("./Dashboard"));
const Settings = lazy(() => import("./Settings"));
function App() {
const [page, setPage] = useState("dashboard");
return (
<div>
<button onClick={() => setPage("dashboard")}>Dashboard</button>
<button onClick={() => setPage("settings")}>Settings</button>
<Suspense fallback={<div>Loading page...</div>}>
{page === "dashboard" ? <Dashboard /> : <Settings />}
</Suspense>
</div>
);
}Line by line:
lazy(() => import("./Dashboard"))— LoadsDashboard.jsas a separate chunk only when React tries to render it.<Suspense fallback={<div>Loading...</div>}>— Wraps lazy components. While the chunk loads, React shows thefallback. When done, it replaces it with the actual component.
React 18+: useTransition
import { useTransition } from "react";
function SearchPage() {
const [isPending, startTransition] = useTransition();
const [query, setQuery] = useState("");
const handleChange = (e) => {
startTransition(() => setQuery(e.target.value)); // low priority
};
return (
<div>
<input onChange={handleChange} />
{isPending && <Spinner />}
<SearchResults query={query} />
</div>
);
}useTransition marks state updates as low priority. React can interrupt them for urgent updates (keystrokes), keeping the UI responsive.
HOCs, Render Props & forwardRef
These are older patterns largely replaced by custom hooks, but you’ll encounter them in legacy codebases and libraries.
Higher-Order Components (HOC)
An HOC takes a component and returns an enhanced one:
function withLoading(WrappedComponent) {
return function WithLoading({ isLoading, ...props }) {
if (isLoading) return <div className="spinner">Loading...</div>;
return <WrappedComponent {...props} />;
};
}
const UserListWithLoading = withLoading(UserList);Render Props
A function prop that the component calls to determine what to render:
function MouseTracker({ render }) {
const [position, setPosition] = useState({ x: 0, y: 0 });
useEffect(() => {
const handler = (e) => setPosition({ x: e.clientX, y: e.clientY });
window.addEventListener("mousemove", handler);
return () => window.removeEventListener("mousemove", handler);
}, []);
return render(position);
}
<MouseTracker render={({ x, y }) => <h1>Position: ({x}, {y})</h1>} />forwardRef
Lets a parent access a DOM element inside a child:
const FancyInput = forwardRef(function FancyInput(props, ref) {
return <input ref={ref} style={{ border: "2px solid blue" }} {...props} />;
});
function Parent() {
const inputRef = useRef(null);
return (
<>
<FancyInput ref={inputRef} />
<button onClick={() => inputRef.current.focus()}>Focus</button>
</>
);
}Reconciliation & Keys
Reconciliation is React’s diffing algorithm — how it decides what to update in the DOM.
How it works:
- React builds a new Virtual DOM tree when state changes
- It compares this with the previous Virtual DOM tree
- Different element types (
<div>→<span>) → React rebuilds the entire subtree - Same element type → React updates only the changed attributes
- For lists, keys tell React which items moved, were added, or removed
{todos.map(todo => (
<li key={todo.id}>{todo.text}</li> // ✅ Stable unique ID
// <li key={todo.index}>...</li> // ❌ Index breaks with reordering
))}Strict Mode
StrictMode is a development-only wrapper that highlights potential problems:
createRoot(document.getElementById("root")).render(
<StrictMode><App /></StrictMode>
);What it checks: Unsafe lifecycle methods, legacy APIs, missing effect cleanup (by double-invoking effects), and render-phase side effects.
Common Mistakes
1. Using error boundaries for event handlers
Error boundaries don’t catch errors in event handlers. Use try/catch:
const handleClick = () => { try { doSomethingRisky(); } catch (err) { console.error(err); } };2. Forgetting to clean up createPortal
Portals render outside the parent DOM tree. If you mount a portal and don’t unmount it via state, the DOM elements stay orphaned.
3. Wrapping everything in Suspense
Suspense only works with lazy() or data fetching libraries that support it. Regular imports don’t trigger Suspense.
4. Overusing HOCs
Multiple HOCs create “wrapper hell” — deeply nested wrappers that are hard to debug. Custom hooks are cleaner.
5. Not providing a fallback for Suspense
The fallback prop is not optional. Always provide a meaningful loading state.
6. Forgetting to reset error boundary state
After an error, the boundary shows the fallback forever unless you provide a reset mechanism.
7. Using array index as key
Index-as-key causes React to reuse the wrong component instances when items are reordered.
Practice Questions
Why must error boundaries be class components?
Because they require
componentDidCatchorgetDerivedStateFromError, which are only available in class components. React is working on a hook-based equivalent.What is the difference between createPortal and normal rendering?
createPortalrenders into a different DOM node (outside the parent), but the component stays in the React tree — context, events, and props still follow React’s hierarchy.When should you use React.lazy?
For route-level code splitting: load each page’s component only when the user navigates to it. Avoid lazy-loading small or frequently-used components.
What problem does forwardRef solve?
By default, refs aren’t passed to child components.
forwardRefexplicitly lets a child expose a DOM node to its parent — useful for focusing inputs or measuring elements.How does reconciliation handle list reordering?
React compares old and new lists using keys. Items with the same key are moved. New keys are inserted. Missing keys are removed.
Challenge
Build a Tooltip component using createPortal that renders near a target element, positions dynamically based on the target’s bounding rect, shows on hover, and handles window resize.
FAQ
Try It Yourself
Edit this sandbox with an Error Boundary and portal-based modal:
What’s Next
| Lesson | Description |
|---|---|
| https://tutorials.dodatech.com/frameworks/react/reference/ | Complete cheatsheet for React APIs and hooks |
| Next.js Full-Stack React | Server-side rendering, routing, data fetching |
| TypeScript for React | Type-safe components and patterns |
| Node.js Backend APIs | Build REST APIs for your React frontend |
Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.
What’s Next
Congratulations on completing this React Advanced tutorial! Here’s where to go from here:
- Practice daily — Consistency is more important than long study sessions
- Build a project — Apply what you learned by building something real
- Explore related topics — Check out other tutorials in the same category
- Join the community — Discuss with other learners and share your progress
Remember: every expert was once a beginner. Keep coding!
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro