// useMemo: Caches a return value
const expensiveValue = useMemo(() => compute(a, b), [a, b]);
// useCallback: Caches a function definition
const handleClick = useCallback(() => {
doSomething(a);
}, [a]);
Real World Example
// Preventing Child Re-renders
const Parent = () => {
// Without useCallback, this function is new every render
// causing Child (if memoized) to re-render anyway.
const handleSelect = useCallback((id) => setSelected(id), []);
return <MemoizedChild onSelect={handleSelect} />;
};
Both are for memoization (caching) to prevent unnecessary re-computations or re-renders.
const expensiveValue = useMemo(() => compute(a, b), [a, b]);
const handleClick = useCallback(() => { ... }, []);
Why use useCallback? To maintain referential equality when passing functions to child components wrapped in React.memo.
const MyComponent = React.memo(function MyComponent(props) {
/* render using props */
});
Real World Example
// List Item Optimization
const ListItem = React.memo(({ item, onToggle }) => {
console.log("Render Item", item.id);
return <li onClick={() => onToggle(item.id)}>{item.text}</li>;
});
// Only re-renders if 'item' or 'onToggle' changes.
A Higher Order Component (HOC) that skips re-rendering a component if its props have not changed.
const Child = React.memo(({ onClick }) => {
console.log("Render Child");
return <button onClick={onClick}>Click</button>;
});
// Problem:
<Page user={user} /> -> <Header user={user} /> -> <Avatar user={user} />
// Solution 1 (Composition):
<Page header={<Header avatar={<Avatar user={user} />} />} />
// Solution 2 (Context):
<UserContext.Provider value={user}>...</UserContext.Provider>
Real World Example
// Theme Context
const ThemeContext = createContext('light');
// Any deep child can access theme without passing props
const theme = useContext(ThemeContext);
Passing data through many layers of components that don't need it.
Solutions:
<Layout header={<Header user={user} />} />.class ErrorBoundary extends React.Component {
state = { hasError: false };
static getDerivedStateFromError(error) { return { hasError: true }; }
componentDidCatch(error, info) { logError(error, info); }
render() {
if (this.state.hasError) return <h1>Something went wrong.</h1>;
return this.props.children;
}
}
Real World Example
// Wrapping major sections
<ErrorBoundary>
<Feed />
</ErrorBoundary>
<ErrorBoundary>
<Sidebar />
</ErrorBoundary>
// If Feed crashes, Sidebar still works.
React components that catch JavaScript errors anywhere in their child component tree, log those errors, and display a fallback UI.
Note: Error Boundaries must be Class Components (currently no Hook equivalent).
class ErrorBoundary extends React.Component {
state = { hasError: false };
static getDerivedStateFromError(error) { return { hasError: true }; }
render() {
if (this.state.hasError) return <h1>Something went wrong.</h1>;
return this.props.children;
}
}
const OtherComponent = React.lazy(() => import('./OtherComponent'));
function MyComponent() {
return (
<Suspense fallback={<div>Loading...</div>}>
<OtherComponent />
</Suspense>
);
}
Real World Example
// Route-based splitting
<Routes>
<Route path="/" element={<Home />} />
<Route path="/admin" element={
<Suspense fallback={<Spinner />}>
<AdminPanel />
</Suspense>
} />
</Routes>
Splitting the bundle into smaller chunks so the user only downloads what they need for the current page. Implemented via React.lazy and dynamic import().
// Automatic Batching
setTimeout(() => {
setCount(c => c + 1);
setFlag(f => !f);
// React 18: Only 1 re-render (batched)
// React 17: 2 re-renders
}, 1000);
Real World Example
// Keeping UI responsive during heavy updates
const [isPending, startTransition] = useTransition();
function handleChange(e) {
const value = e.target.value;
setInputValue(value); // Urgent: Update input immediately
startTransition(() => {
setFilter(value); // Non-urgent: Filter heavy list
});
}
The ability for React to work on multiple tasks at once. It can pause a heavy render to handle a high-priority user interaction (like typing), then resume the heavy render. Features: useTransition, useDeferredValue.
// Server
const html = ReactDOMServer.renderToString(<App />);
// Client
ReactDOM.hydrateRoot(document.getElementById('root'), <App />);
Real World Example
// Next.js handles this automatically.
// Hydration mismatch error occurs if server HTML != client HTML
// e.g., rendering Date.now() or Math.random() directly.
The process where React attaches event listeners to the static HTML generated by the server (SSR), making the page interactive.
// 1. Action
const action = { type: 'ADD_TODO', text: 'Buy milk' };
// 2. Dispatcher sends action to Store
// 3. Store updates state
// 4. View updates based on Store
Real World Example
// Redux is the most popular implementation
dispatch(addTodo('Buy milk'));
// -> Reducer calculates new state
// -> Components re-render
Action -> Dispatcher -> Store -> View. The predecessor to Redux. It enforces one-way data flow to make state predictable.
function withBorder(WrappedComponent) {
return (props) => (
<div style={{ border: '1px solid red' }}>
<WrappedComponent {...props} />
</div>
);
}
Real World Example
// Authentication HOC
const withAuth = (Component) => (props) => {
const { user } = useAuth();
if (!user) return <Login />;
return <Component {...props} />;
};
export default withAuth(Dashboard);
A function that takes a component and returns a new component with enhanced functionality. It's a pattern for reusing component logic (like mixins in the old days).
Note: Custom Hooks have largely replaced HOCs for logic reuse, but HOCs are still useful for injecting UI or props.
<MouseTracker render={({ x, y }) => (
<h1>The mouse is at {x}, {y}</h1>
)} />
Real World Example
// Formik (older versions) used this heavily
<Formik>
{({ values, handleChange }) => (
<form>
<input value={values.email} onChange={handleChange} />
</form>
)}
</Formik>
A technique for sharing code between React components using a prop whose value is a function. The component calls this function to determine what to render.
1. State changes.
2. React creates a new Virtual DOM tree.
3. React compares (diffs) the new tree with the old one.
4. React calculates the minimum number of changes needed.
5. React updates the real DOM (Reconciliation).
A lightweight JavaScript representation of the actual DOM. React uses it to minimize direct manipulation of the DOM, which is slow. By comparing the new Virtual DOM with the previous one (Diffing), React updates only the parts of the real DOM that actually changed (Reconciliation).
// useEffect: Runs after paint (Async)
useEffect(() => {
console.log("Visible to user");
});
// useLayoutEffect: Runs before paint (Sync)
useLayoutEffect(() => {
console.log("Blocks visual update");
});
Real World Example
// Measuring DOM elements
useLayoutEffect(() => {
const { height } = ref.current.getBoundingClientRect();
setHeight(height);
}, []);
// Prevents "flicker" where user sees 0 height then jumps to real height.
useEffect: Runs asynchronously after the render is painted to the screen. Good for data fetching, subscriptions.
useLayoutEffect: Runs synchronously after DOM mutations but before the browser paints. Use this to measure DOM layout (e.g., getting scroll position or element width) to prevent visual flickering.
const CustomInput = forwardRef((props, ref) => {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
alertValue: () => alert(inputRef.current.value)
}));
return <input ref={inputRef} />;
});
Real World Example
// Parent Component
const ref = useRef();
// ...
<CustomInput ref={ref} />
// ...
ref.current.alertValue(); // Only exposes this method, not full DOM node
It allows you to customize the instance value that is exposed to parent components when using ref. Typically used with forwardRef.
useImperativeHandle(ref, () => ({
focus: () => inputRef.current.focus(),
scroll: () => inputRef.current.scrollIntoView()
}));
const deferredQuery = useDeferredValue(query);
// React renders with 'query' (urgent)
// Then re-renders with 'deferredQuery' (non-urgent)
Real World Example
// Search Results
const [query, setQuery] = useState('');
const deferredQuery = useDeferredValue(query);
return (
<>
<input value={query} onChange={e => setQuery(e.target.value)} />
<HeavyList filter={deferredQuery} />
</>
);
// Input stays responsive, List updates later.
It allows you to defer updating a part of the UI. It's like a built-in debounce. React will first render with the old value, then render with the new value in the background.
Useful for search inputs where the list filtering is heavy.
const [isPending, startTransition] = useTransition();
startTransition(() => {
// State updates here are low priority
setPage(newPage);
});
Real World Example
// Tab Switching
{isPending && <Spinner />}
<button onClick={() => startTransition(() => setTab('photos'))}>
Photos
</button>
// If 'Photos' is heavy, UI remains interactive during render.
It lets you mark a state update as a "transition" (low priority). If the user interacts while the transition is pending, React can interrupt it.
const [isPending, startTransition] = useTransition();
startTransition(() => {
setQuery(input); // This update is low priority
});
const id = useId();
return (
<>
<label htmlFor={id}>Name</label>
<input id={id} />
</>
);
Real World Example
// Ensuring unique IDs in repeated components
// Works consistently between Server (SSR) and Client (Hydration)
// avoiding "prop mismatch" errors.
Generates unique IDs that are stable across server and client (hydration safe). Essential for connecting labels to inputs (htmlFor) and ARIA attributes.
A hook for subscribing to external data sources (like Redux store or browser history) in a way that is compatible with concurrent rendering features. It replaces useEffect for subscriptions in libraries.
It is a lightweight copy of the actual DOM kept in memory. React creates a tree of objects (Fiber nodes) representing the UI. When state changes, it creates a new tree, diffs it with the old one, and computes the minimal set of changes (Reconciliation) to apply to the real DOM.
The process of comparing the current tree with the new tree to determine what parts of the UI need to be updated. React uses a heuristic O(n) algorithm based on assumptions like: two elements of different types will produce different trees, and keys are stable.
The reimplementation of React's core algorithm (introduced in v16). Its main goal was to enable incremental rendering: the ability to split rendering work into chunks and spread it out over multiple frames.
Keys help React identify which items have changed, been added, or removed. Without keys (or using index as key), React might reuse DOM elements incorrectly, leading to state bugs or performance issues when the list order changes.
React groups multiple state updates into a single re-render for better performance. Before React 18, only updates inside event handlers were batched. In React 18, Automatic Batching covers promises, timeouts, and native event handlers too.
ReactDOM.createPortal(child, container) lets you render a child component into a DOM node that exists outside the hierarchy of the parent component. Essential for Modals, Tooltips, and Dropdowns to avoid z-index or overflow issues.
"use client" directive.Add "use client" at the top of a file if the component needs:
Yes. This is the standard pattern. A Server Component (Page) renders a Client Component (Button).
No, not directly. Because Client Components run in the browser, they cannot execute server-only code. However, you can pass a Server Component as a child (prop) to a Client Component.
A pattern where components work together to share state implicitly, usually via Context. Example: <Select> and <Option>. The parent Select manages the state, and Option communicates with it without explicit prop passing by the user.
A technique for sharing code between components using a prop whose value is a function.
<MouseTracker render={({ x, y }) => (
<h1>The mouse is at {x}, {y}</h1>
)} />
Instead of a component handling all logic internally, it exposes props (like renderItem or components={{ Header: MyHeader }}) to let the consumer customize the rendering logic.
ref. Easier for integrating non-React code.XSS is injecting malicious scripts. React escapes all variables embedded in JSX by default, converting them to strings before rendering. This prevents script injection.
Only when the HTML content has been sanitized (using a library like DOMPurify) to strip out malicious scripts. Never use it with raw user input.
Accessible Rich Internet Applications attributes (e.g., aria-label, aria-expanded). They provide extra semantic information to assistive technologies (screen readers) when standard HTML tags aren't enough.
When a modal opens, focus should move to the modal. Focus should be "trapped" inside the modal (tabbing shouldn't exit it). When closed, focus should return to the element that opened it.
A technique to render only the items currently visible in the viewport (plus a small buffer). Libraries like react-window or react-virtualized are used for lists with thousands of items to maintain performance.
Use the React DevTools Profiler. It shows which components rendered, why they rendered (prop changes, hooks), and how long it took. Look for "wasted renders".
When a Context value changes, all components consuming that context will re-render, even if they only use a part of the data. To mitigate this, split context into smaller contexts or use memoization.
If you define Child inside Parent, Child is redefined on every render of Parent. React treats it as a completely new component type, causing it to unmount and remount (losing state and focus) every time.
A build step (Webpack/Rollup) that removes unused code from the final bundle. It relies on ES6 static module syntax (import/export). If you import a huge library but only use one function, tree shaking attempts to remove the rest.
Changing the key prop of a component forces React to destroy the old instance and create a new one. This is the cleanest way to reset a component's internal state to its initial values.
In development, <React.StrictMode> intentionally invokes render functions, effects, and reducers twice to help detect side effects in places they shouldn't be (like the render phase). It does not happen in production.
In a class constructor, super(props) passes props to the parent React.Component, making this.props available immediately in the constructor. If you just call super(), this.props will be undefined inside the constructor (but fine in other methods).
React uses SyntheticEvents, a cross-browser wrapper around the native event. It also uses Event Delegation: it attaches a single event listener to the root of the document rather than one for every node, improving memory usage.
A library for runtime type checking of props. Component.propTypes = { name: PropTypes.string }. Largely replaced by TypeScript (compile-time checking), but still useful for library authors.
This ensures hooks are called in the same order every render.
1. Use uncontrolled components (refs) or libraries like react-hook-form that minimize re-renders.
2. Isolate state: Move the state of a specific input down into its own component so typing only re-renders that input, not the whole page.
A component that lets you display a fallback (loading spinner) while its children are waiting for something (like lazy loaded code or data fetching). <Suspense fallback={<Spinner />}>...</Suspense>.
Splitting a frontend app into smaller, semi-independent "apps" that can be developed and deployed by different teams, then composed together in the browser (e.g., via Module Federation).