[{"data":1,"prerenderedAt":131},["ShallowReactive",2],{"qa-\u002Freact\u002Fpatterns\u002Frender-props-hoc":3},{"page":4,"siblings":110,"blog":128},{"id":5,"title":6,"body":7,"description":11,"difficulty":14,"extension":15,"framework":16,"frameworkSlug":17,"meta":18,"navigation":19,"order":12,"path":20,"questions":21,"questionsCount":100,"related":101,"seo":102,"seoDescription":103,"stem":104,"subtopic":105,"topic":106,"topicSlug":107,"updated":108,"__hash__":109},"qa\u002Freact\u002Fpatterns\u002Frender-props-hoc.md","Render Props Hoc",{"type":8,"value":9,"toc":10},"minimark",[],{"title":11,"searchDepth":12,"depth":12,"links":13},"",2,[],"medium","md","React","react",{},true,"\u002Freact\u002Fpatterns\u002Frender-props-hoc",[22,27,31,35,39,43,47,51,55,60,64,68,72,76,80,84,88,92,96],{"id":23,"difficulty":24,"q":25,"a":26},"what-is-render-props-pattern","easy","What is the render props pattern and what problem does it solve?","The **render props** pattern is a technique for sharing stateful logic between components\nby passing a **function as a prop**. The host component manages state and calls that\nfunction with the state as arguments, letting the consumer decide what to render.\n\nThe problem it solves is **logic reuse without inheritance**. Before hooks, if two\ncomponents needed the same piece of state (mouse position, window size, data fetching\nstatus) you had three options: copy-paste the logic, lift state up (coupling unrelated\ncomponents), or use render props \u002F HOCs. Render props kept the logic encapsulated in\none place while giving the consumer full rendering control.\n\n```jsx\n\u002F\u002F DataFetcher owns fetching logic; caller decides the UI\nfunction DataFetcher({ url, render }) {\n  const [data, setData] = React.useState(null);\n  const [loading, setLoading] = React.useState(true);\n\n  React.useEffect(() => {\n    fetch(url)\n      .then(r => r.json())\n      .then(d => { setData(d); setLoading(false); });\n  }, [url]);\n\n  \u002F\u002F Delegates rendering entirely to the caller\n  return render({ data, loading });\n}\n\n\u002F\u002F Usage — caller controls the UI\n\u003CDataFetcher\n  url=\"\u002Fapi\u002Fusers\"\n  render={({ data, loading }) =>\n    loading ? \u003CSpinner \u002F> : \u003CUserList users={data} \u002F>\n  }\n\u002F>\n```\n\n**Rule of thumb:** Render props shine when you want to share complex stateful behaviour\nwhile letting each consumer render something different.\n",{"id":28,"difficulty":24,"q":29,"a":30},"function-as-child-vs-render-prop","What is the function-as-child pattern and how does it differ from an explicit render prop?","**Function-as-child** (also called \"children as a function\") is just a render prop\nwhere the prop name is `children` instead of a custom name like `render`. The component\ncalls `props.children(...)` exactly as it would call `props.render(...)`.\n\nThe only differences are:\n- Syntactic: the function lives inside the JSX tag body, which reads more naturally\n  for simple cases.\n- Discoverability: explicit render props (`render`, `renderHeader`, etc.) are visible\n  in prop-types\u002FTypeScript signatures; `children` can surprise readers if they expect\n  a ReactNode.\n\n```jsx\n\u002F\u002F Explicit render prop\n\u003CMouse render={({ x, y }) => \u003CCursor x={x} y={y} \u002F>} \u002F>\n\n\u002F\u002F Function-as-child — identical behaviour, different syntax\n\u003CMouse>\n  {({ x, y }) => \u003CCursor x={x} y={y} \u002F>}\n\u003C\u002FMouse>\n\n\u002F\u002F Inside Mouse — both approaches call the same way\nfunction Mouse({ children }) {\n  const [pos, setPos] = React.useState({ x: 0, y: 0 });\n  return (\n    \u003Cdiv onMouseMove={e => setPos({ x: e.clientX, y: e.clientY })}>\n      {children(pos)}  {\u002F* or props.render(pos) *\u002F}\n    \u003C\u002Fdiv>\n  );\n}\n```\n\n**Rule of thumb:** Use an explicit render prop when you need multiple render slots\n(`renderHeader`, `renderFooter`); use `children` when there is only one slot and\nthe JSX nesting reads naturally.\n",{"id":32,"difficulty":24,"q":33,"a":34},"what-is-hoc","What is a Higher-Order Component (HOC) and what is its signature?","A **Higher-Order Component** is a function that takes a component and returns a new,\nenhanced component. It is the React adaptation of the higher-order function concept —\njust as `Array.map` takes a function and returns a new array, an HOC takes a component\nand returns a component.\n\nThe canonical signature is:\n\n```ts\n\u002F\u002F Generic signature: WrappedComponent in, EnhancedComponent out\nfunction withSomething\u003CP extends object>(\n  WrappedComponent: React.ComponentType\u003CP>\n): React.ComponentType\u003CP & InjectedProps> {\n\n  function WithSomething(props: P) {\n    const injected = useSomethingLogic(); \u002F\u002F HOC-owned logic\n    return \u003CWrappedComponent {...props} {...injected} \u002F>;\n  }\n\n  \u002F\u002F Naming convention: prefix with \"with\"\n  WithSomething.displayName =\n    `WithSomething(${WrappedComponent.displayName ?? WrappedComponent.name ?? 'Component'})`;\n\n  return WithSomething;\n}\n```\n\nKey points:\n- The **naming convention** is `withXxx` (camelCase, \"with\" prefix) — e.g., `withAuth`,\n  `withTheme`, `withLogger`.\n- Set `displayName` so React DevTools shows `WithAuth(Dashboard)` rather than an\n  anonymous component.\n- The HOC **does not mutate** the wrapped component; it wraps it.\n\n**Rule of thumb:** If a function takes a component and returns a component, it is an\nHOC; name it `withXxx` and set `displayName`.\n",{"id":36,"difficulty":14,"q":37,"a":38},"hoc-composition","How do you compose multiple HOCs and what utility makes that cleaner?","You can nest HOC calls directly, but deep nesting reads inside-out and is hard to\nmaintain. The standard solution is a **`compose` utility** (available in lodash, Redux,\nRamda, or trivial to write) that applies functions right-to-left so the reading order\nmatches the wrapping order.\n\n```js\n\u002F\u002F Naive nesting — reads inside-out, breaks easily when reordering\nexport default withLogger(withAuth(withTheme(MyComponent)));\n\n\u002F\u002F compose applies right-to-left: theme → auth → logger (outermost last)\nimport { compose } from 'redux'; \u002F\u002F or lodash\u002Ffp\n\nconst enhance = compose(\n  withLogger,   \u002F\u002F applied last (outermost)\n  withAuth,     \u002F\u002F applied second\n  withTheme,    \u002F\u002F applied first (innermost, closest to component)\n);\n\nexport default enhance(MyComponent);\n\n\u002F\u002F Minimal compose implementation for reference\nconst compose = (...fns) => x => fns.reduceRight((v, f) => f(v), x);\n```\n\nOrder matters: the outermost HOC's props arrive first. If `withAuth` reads a `theme`\nprop injected by `withTheme`, then `withTheme` must be applied before `withAuth`\n(listed after it in `compose`).\n\n**Rule of thumb:** Use `compose` when stacking three or more HOCs; keep the list in\ninside-out (most-specific-first) order so execution matches your mental model.\n",{"id":40,"difficulty":14,"q":41,"a":42},"cross-cutting-concerns-hoc","Name three cross-cutting concerns that HOCs handle well and explain one in detail.","Classic cross-cutting concerns suited to HOCs:\n\n1. **Authentication \u002F authorisation guards** — redirect unauthenticated users before\n   rendering a page component.\n2. **Analytics \u002F logging** — record component mount, unmount, and prop changes without\n   touching business logic components.\n3. **Error boundaries** — wrap any component with standardised error UI.\n\n`withAuth` example in detail:\n\n```jsx\n\u002F\u002F withAuth.jsx — auth guard HOC\nimport { Navigate } from 'react-router-dom';\nimport { useAuth } from '..\u002Fcontext\u002FAuthContext';\n\nfunction withAuth(WrappedComponent, requiredRole = null) {\n  function WithAuth(props) {\n    const { user, loading } = useAuth();\n\n    if (loading) return \u003CSpinner \u002F>;\n    if (!user) return \u003CNavigate to=\"\u002Flogin\" replace \u002F>;\n    if (requiredRole && user.role !== requiredRole)\n      return \u003CNavigate to=\"\u002Fforbidden\" replace \u002F>;\n\n    \u002F\u002F User is authenticated (and has the right role) — render component\n    return \u003CWrappedComponent {...props} \u002F>;\n  }\n\n  WithAuth.displayName = `WithAuth(${WrappedComponent.displayName ?? WrappedComponent.name})`;\n  return WithAuth;\n}\n\n\u002F\u002F Usage\nexport default withAuth(AdminDashboard, 'admin');\n```\n\n**Rule of thumb:** HOCs are ideal for cross-cutting concerns that need to intercept\nrendering (guards, error boundaries) or observe lifecycle events (logging) without\naltering the wrapped component's own logic.\n",{"id":44,"difficulty":14,"q":45,"a":46},"prop-collision-hoc","What is prop collision in HOCs and how do you prevent it?","**Prop collision** occurs when an HOC injects a prop whose name clashes with a prop\nthe wrapped component (or a parent) already uses. The HOC's value silently wins because\nit is spread last (or first), overwriting the intended value and causing subtle bugs.\n\nPrevention strategies:\n\n```jsx\n\u002F\u002F BAD — HOC spreads injected props after ownProps; \"user\" collision\nfunction withUser(WrappedComponent) {\n  return function(props) {\n    const user = useCurrentUser();\n    \u002F\u002F If parent passes user={guestUser}, HOC's user overwrites it silently\n    return \u003CWrappedComponent {...props} user={user} \u002F>;\n  };\n}\n\n\u002F\u002F GOOD — namespace injected props under a dedicated key\nfunction withUser(WrappedComponent) {\n  return function({ injectedUser, ...ownProps }) {\n    const user = useCurrentUser();\n    \u002F\u002F Injected prop has a unique name; no risk of shadowing parent props\n    return \u003CWrappedComponent {...ownProps} currentUser={user} \u002F>;\n  };\n}\n\n\u002F\u002F BEST — use TypeScript to make the contract explicit\ntype WithUserProps = { currentUser: User };\nfunction withUser\u003CP extends WithUserProps>(\n  WrappedComponent: React.ComponentType\u003CP>\n) { \u002F* ... *\u002F }\n```\n\nOther strategies: document all injected prop names, prefix them (`_injectedUser`),\nor prefer hooks (which do not pollute the prop namespace at all).\n\n**Rule of thumb:** Never spread HOC-injected props with generic names; use descriptive,\nnamespaced names and make them explicit in TypeScript types.\n",{"id":48,"difficulty":14,"q":49,"a":50},"why-hooks-replaced-render-props-hocs","Why did custom hooks largely replace render props and HOCs?","Custom hooks solve the same logic-reuse problem without the downsides of both patterns:\n\n| Issue | Render props \u002F HOCs | Custom hooks |\n|---|---|---|\n| **Extra DOM nodes** | Yes — every HOC \u002F render-prop host adds a wrapper in the tree | No — hooks run inside the calling component |\n| **Prop drilling \u002F collision** | HOCs inject props that can clash | Hooks return values via destructuring — no prop namespace |\n| **Composability** | HOCs compose left-to-right but reading order is confusing | Just call multiple hooks sequentially |\n| **TypeScript** | Generics + conditional types needed for good inference | Plain function return types — straightforward |\n| **DevTools clarity** | Deep \"wrapper hell\" in component tree | Flat component tree |\n\n```jsx\n\u002F\u002F Pre-hooks: render prop for mouse position\n\u003CMouseTracker render={({ x, y }) => \u003CCrosshair x={x} y={y} \u002F>} \u002F>\n\n\u002F\u002F Post-hooks: extract into a custom hook — same logic, no wrapper\nfunction useMousePosition() {\n  const [pos, setPos] = React.useState({ x: 0, y: 0 });\n  React.useEffect(() => {\n    const handler = e => setPos({ x: e.clientX, y: e.clientY });\n    window.addEventListener('mousemove', handler);\n    return () => window.removeEventListener('mousemove', handler);\n  }, []);\n  return pos;\n}\n\nfunction Crosshair() {\n  const { x, y } = useMousePosition(); \u002F\u002F clean, no wrapper\n  return \u003Cdiv style={{ top: y, left: x }} \u002F>;\n}\n```\n\n**Rule of thumb:** Reach for a custom hook first; only use render props or HOCs when\nyou need to control rendering structure (render prop) or integrate with class components\nor third-party code that cannot use hooks (HOC).\n",{"id":52,"difficulty":14,"q":53,"a":54},"when-render-props-still-right-tool","When are render props still the right tool even in a hooks world?","Render props remain the best choice in several scenarios:\n\n1. **Controlled render delegation** — when the parent needs to decide *what* renders\n   inside a complex host (e.g., a virtualised list that injects row index\u002Fstyle and\n   expects the caller to return the row JSX).\n2. **Class component consumers** — hooks cannot run inside class components; an HOC\n   or render-prop component is the only way to feed hook-based logic into them.\n3. **Library APIs** — `react-router` (`\u003CRoute render>` in v5), `formik` (`\u003CField>`),\n   and `react-window` (`\u003CFixedSizeList>`) expose render props because they cannot\n   know your render tree ahead of time.\n\n```jsx\n\u002F\u002F react-window — render prop is the correct API; hooks cannot replace it here\nimport { FixedSizeList } from 'react-window';\n\nfunction VirtualList({ items }) {\n  const Row = ({ index, style }) => (\n    \u002F\u002F style MUST be applied for virtualisation to work\n    \u003Cdiv style={style}>{items[index].name}\u003C\u002Fdiv>\n  );\n\n  return (\n    \u003CFixedSizeList height={400} itemCount={items.length} itemSize={35} width=\"100%\">\n      {Row}\n    \u003C\u002FFixedSizeList>\n  );\n}\n```\n\n**Rule of thumb:** Render props are still the right tool when a library needs you to\nsupply a JSX factory, or when the component consuming your logic is a class component.\n",{"id":56,"difficulty":57,"q":58,"a":59},"static-methods-hocs","hard","Why do HOCs lose static methods from the wrapped component and how do you fix it?","A wrapped component's **static methods** (e.g., `getLayout`, `fetchData`,\n`propTypes`) are defined on the original component object. The HOC returns a *new*\nfunction object that does not automatically copy those statics, so callers that\nlook up `EnhancedComponent.getLayout` find `undefined`.\n\nThe fix is **`hoist-non-react-statics`**, a small utility that copies all non-React\nstatics from the wrapped component to the wrapper:\n\n```jsx\nimport hoistNonReactStatics from 'hoist-non-react-statics';\n\nfunction withLogger(WrappedComponent) {\n  function WithLogger(props) {\n    React.useEffect(() => { console.log('mounted'); }, []);\n    return \u003CWrappedComponent {...props} \u002F>;\n  }\n\n  WithLogger.displayName =\n    `WithLogger(${WrappedComponent.displayName ?? WrappedComponent.name})`;\n\n  \u002F\u002F Copy getLayout, fetchData, propTypes, etc. from original\n  hoistNonReactStatics(WithLogger, WrappedComponent);\n\n  return WithLogger;\n}\n\n\u002F\u002F Statics survive the wrapping\nWithLogger.getLayout === WrappedComponent.getLayout; \u002F\u002F true\n```\n\nReact-specific statics (`defaultProps`, `propTypes`, `contextType`, `displayName`,\n`getDerivedStateFromProps`, etc.) are intentionally **not** hoisted — the library\nhas a hardcoded exclusion list for them.\n\n**Rule of thumb:** Always call `hoistNonReactStatics(Wrapper, WrappedComponent)`\ninside every HOC factory so page-level statics (especially Next.js `getLayout`) are\nnot silently dropped.\n",{"id":61,"difficulty":57,"q":62,"a":63},"forwardref-in-hocs","How do you forward refs through a HOC?","HOCs break `ref` forwarding by default because `ref` is not a real prop — it is\nhandled by React's reconciler and does not appear in `props`. A ref attached to\nthe HOC wrapper points at the wrapper, not the underlying DOM node or component.\n\nThe fix is `React.forwardRef`:\n\n```jsx\nfunction withFocusRing(WrappedComponent) {\n  \u002F\u002F forwardRef creates a component that passes its ref down\n  const WithFocusRing = React.forwardRef(function(props, ref) {\n    const [focused, setFocused] = React.useState(false);\n    return (\n      \u003Cdiv className={focused ? 'ring' : ''}>\n        \u003CWrappedComponent\n          {...props}\n          ref={ref}           \u002F\u002F forward the ref to the real component\n          onFocus={() => setFocused(true)}\n          onBlur={() => setFocused(false)}\n        \u002F>\n      \u003C\u002Fdiv>\n    );\n  });\n\n  WithFocusRing.displayName =\n    `WithFocusRing(${WrappedComponent.displayName ?? WrappedComponent.name})`;\n\n  hoistNonReactStatics(WithFocusRing, WrappedComponent);\n  return WithFocusRing;\n}\n\n\u002F\u002F Caller gets a ref to the real input, not the wrapper div\nconst EnhancedInput = withFocusRing(React.forwardRef((props, ref) =>\n  \u003Cinput ref={ref} {...props} \u002F>\n));\nconst ref = React.createRef();\n\u003CEnhancedInput ref={ref} \u002F>;\n```\n\n**Rule of thumb:** Wrap the inner component in `React.forwardRef` and thread the\n`ref` argument through to `WrappedComponent`; always pair this with `hoistNonReactStatics`.\n",{"id":65,"difficulty":14,"q":66,"a":67},"hoc-performance-implications","What are the performance implications of HOCs and render props?","Both patterns can introduce unnecessary re-renders if not handled carefully.\n\n**HOC issues:**\n- Each HOC in a composition chain is a separate React component — `React.memo` on\n  the inner component does not prevent the wrapper from re-rendering.\n- If the HOC creates a new object or function on every render and passes it as a\n  prop, the wrapped component will see a new reference and re-render even if the\n  underlying data did not change.\n\n**Render prop issues (the classic \"always re-renders\" problem):**\n```jsx\n\u002F\u002F BAD — inline arrow function creates a new function reference every render\n\u002F\u002F → Mouse re-renders, calls render(), child always re-renders\n\u003CMouse render={({ x, y }) => \u003CCrosshair x={x} y={y} \u002F>} \u002F>\n\n\u002F\u002F GOOD — stable function reference with useCallback (or define outside render)\nfunction App() {\n  const renderCrosshair = React.useCallback(\n    ({ x, y }) => \u003CCrosshair x={x} y={y} \u002F>,\n    [] \u002F\u002F stable — no deps\n  );\n  return \u003CMouse render={renderCrosshair} \u002F>;\n}\n```\n\nFor HOCs, apply `React.memo` to *each layer* independently, or memoize injected\nvalues inside the HOC with `useMemo`\u002F`useCallback`.\n\n**Rule of thumb:** Stabilise function references passed as render props with\n`useCallback`; inside HOCs, memoize injected values to avoid cascading re-renders.\n",{"id":69,"difficulty":57,"q":70,"a":71},"typescript-generics-hoc","How do you type a HOC in TypeScript so that the wrapped component's props are preserved?","The key is using **generics** to capture the wrapped component's prop type and subtract\nthe injected props so callers do not have to re-supply them.\n\n```tsx\n\u002F\u002F Props injected by the HOC\ninterface WithAuthProps {\n  currentUser: User;\n}\n\n\u002F\u002F P = full props of wrapped component\n\u002F\u002F We subtract WithAuthProps so callers don't pass currentUser manually\nfunction withAuth\u003CP extends WithAuthProps>(\n  WrappedComponent: React.ComponentType\u003CP>\n): React.ComponentType\u003COmit\u003CP, keyof WithAuthProps>> {\n\n  function WithAuth(props: Omit\u003CP, keyof WithAuthProps>) {\n    const { user } = useAuth();\n    if (!user) return \u003CNavigate to=\"\u002Flogin\" replace \u002F>;\n\n    \u002F\u002F Cast needed because TS can't fully verify the Omit + spread\n    return \u003CWrappedComponent {...(props as P)} currentUser={user} \u002F>;\n  }\n\n  WithAuth.displayName =\n    `WithAuth(${WrappedComponent.displayName ?? WrappedComponent.name ?? 'Component'})`;\n\n  hoistNonReactStatics(WithAuth, WrappedComponent);\n  return WithAuth;\n}\n\n\u002F\u002F Usage — TypeScript knows currentUser is supplied by HOC, not by caller\ninterface DashboardProps extends WithAuthProps { title: string; }\nfunction Dashboard({ currentUser, title }: DashboardProps) { \u002F* ... *\u002F }\n\nconst ProtectedDashboard = withAuth(Dashboard);\n\u003CProtectedDashboard title=\"Home\" \u002F> \u002F\u002F ✓ — no currentUser required\n```\n\n**Rule of thumb:** Use `P extends InjectedProps` on the generic and `Omit\u003CP, keyof InjectedProps>`\non the output type so the HOC's additions are invisible to callers.\n",{"id":73,"difficulty":14,"q":74,"a":75},"testing-hoc-wrapped-components","What are two strategies for testing a HOC-wrapped component?","**Strategy 1 — Test the wrapped component in isolation.**\nExport the unwrapped component as a named export alongside the default HOC-wrapped\nexport. In tests, import the unwrapped version and pass the injected props manually.\nThis keeps unit tests free of HOC side-effects.\n\n**Strategy 2 — Mock the HOC's dependencies and test the full composed component.**\nUseful for integration tests that need to verify the HOC's interception logic (e.g.,\nthat `withAuth` redirects unauthenticated users).\n\n```tsx\n\u002F\u002F userList.tsx — dual export pattern\nexport function UserList({ currentUser }: WithAuthProps & { currentUser: User }) {\n  return \u003Cdiv>{currentUser.name}\u003C\u002Fdiv>;\n}\nexport default withAuth(UserList); \u002F\u002F default = enhanced\n\n\u002F\u002F userList.test.tsx\nimport { UserList } from '.\u002FuserList'; \u002F\u002F named — no HOC\nimport { render, screen } from '@testing-library\u002Freact';\n\ntest('renders user name', () => {\n  render(\u003CUserList currentUser={{ name: 'Alice', role: 'user' }} \u002F>);\n  expect(screen.getByText('Alice')).toBeInTheDocument();\n});\n\n\u002F\u002F Integration test — mock auth context\nimport WrappedUserList from '.\u002FuserList'; \u002F\u002F default — HOC included\ntest('redirects when unauthenticated', () => {\n  mockUseAuth({ user: null, loading: false });\n  render(\u003CWrappedUserList \u002F>, { wrapper: MemoryRouterWrapper });\n  expect(mockNavigate).toHaveBeenCalledWith('\u002Flogin', expect.anything());\n});\n```\n\n**Rule of thumb:** Always export the base component as a named export so unit tests\ncan bypass the HOC; use integration tests only when you need to test the HOC's own\nbehaviour.\n",{"id":77,"difficulty":24,"q":78,"a":79},"render-props-vs-hoc-choice","When would you choose a render prop over a HOC, and vice-versa?","The decision hinges on who controls the rendered output and how the logic is consumed.\n\n| Choose **render prop** when… | Choose **HOC** when… |\n|---|---|\n| The consumer needs to render different UI per use-site | Every consumer renders roughly the same structure |\n| You want explicit, visible data flow in JSX | You want transparent wrapping (consumer doesn't change) |\n| You need to compose render slots (header, body, footer) | You need to decorate a whole page\u002Froute component |\n| The consuming component is a function component | You need to wrap class components with consistent behaviour |\n\n```jsx\n\u002F\u002F Render prop — consumer decides the UI\n\u003CToggle>\n  {({ on, toggle }) =>\n    on\n      ? \u003CButton onClick={toggle}>Turn Off\u003C\u002FButton>\n      : \u003CButton onClick={toggle}>Turn On\u003C\u002FButton>\n  }\n\u003C\u002FToggle>\n\n\u002F\u002F HOC — consumer is unaware of the wrapping\nconst AnalyticsButton = withPageViewLogger(Button);\n\u002F\u002F Button code is unchanged; analytics are injected invisibly\n\u003CAnalyticsButton onClick={handleClick}>Buy Now\u003C\u002FAnalyticsButton>\n```\n\n**Rule of thumb:** Use render props when the consumer owns the output markup; use\nHOCs when you want to transparently add behaviour to an existing component.\n",{"id":81,"difficulty":24,"q":82,"a":83},"wrapper-hell","What is \"wrapper hell\" and how do you recognise it in React DevTools?","**Wrapper hell** (also called \"HOC hell\" or \"pyramid of doom\") describes a component\ntree where many HOC layers surround a single component, making DevTools output nearly\nunreadable and stack traces hard to follow.\n\nIn React DevTools you see something like:\n\n```\n\u002F\u002F Component tree with 5 HOCs — hard to navigate\n\u003CWithRouter>\n  \u003CWithTheme>\n    \u003CWithAuth>\n      \u003CWithLogger>\n        \u003CWithErrorBoundary>\n          \u003CDashboard \u002F>\n        \u003C\u002FWithErrorBoundary>\n      \u003C\u002FWithLogger>\n    \u003C\u002FWithAuth>\n  \u003C\u002FWithTheme>\n\u003C\u002FWithRouter>\n```\n\nSolutions:\n1. **Replace with hooks** — collapse all five wrappers into hook calls inside `Dashboard`.\n2. **Use `compose`** — at least the source code reads linearly even if the tree is deep.\n3. **Set meaningful `displayName`** on each HOC so DevTools shows `WithAuth(Dashboard)`.\n\n```jsx\n\u002F\u002F After refactoring to hooks — flat tree, same logic\nfunction Dashboard() {\n  const { params } = useRouter();\n  const theme = useTheme();\n  const { user } = useAuth();\n  usePageLogger();\n  useErrorBoundary();\n  \u002F\u002F ...\n}\n```\n\n**Rule of thumb:** If DevTools shows more than two or three wrapper components around\na single leaf, consider collapsing the HOC stack into custom hooks.\n",{"id":85,"difficulty":14,"q":86,"a":87},"hoc-vs-hooks-class-components","Can hooks fully replace HOCs for class components? What is the recommended migration path?","**No** — hooks cannot run inside class components. An HOC (or render prop) remains\nthe only way to inject hook-based logic into a class component. This matters for\nlegacy codebases or third-party libraries still using class components.\n\nThe recommended migration path is **incremental**:\n\n```jsx\n\u002F\u002F Step 1 — write the logic as a hook\nfunction useTheme() {\n  return React.useContext(ThemeContext);\n}\n\n\u002F\u002F Step 2 — create a thin HOC that calls the hook and injects the result\n\u002F\u002F           This bridges class components without rewriting them\nfunction withTheme(WrappedComponent) {\n  function WithTheme(props) {\n    const theme = useTheme(); \u002F\u002F hook runs here (function component)\n    return \u003CWrappedComponent {...props} theme={theme} \u002F>;\n  }\n  WithTheme.displayName = `WithTheme(${WrappedComponent.name})`;\n  return WithTheme;\n}\n\n\u002F\u002F Step 3 — class component uses the HOC today\nclass Toolbar extends React.Component {\n  render() {\n    return \u003Cdiv style={{ background: this.props.theme.bg }}>…\u003C\u002Fdiv>;\n  }\n}\nexport default withTheme(Toolbar);\n\n\u002F\u002F Step 4 — when Toolbar is converted to a function component, drop the HOC\n\u002F\u002F           and call useTheme() directly\n```\n\n**Rule of thumb:** Write all new logic as hooks; wrap in a thin HOC only to bridge\nclass components; remove the HOC shim when the class component is converted.\n",{"id":89,"difficulty":24,"q":90,"a":91},"hoc-display-name-convention","Why is setting displayName on a HOC important and what is the correct format?","React uses `displayName` (or the function name as a fallback) when rendering component\ntrees in **DevTools** and in **error stack traces**. Without it, every HOC-wrapped\ncomponent shows as `Anonymous` or just the HOC function name, making debugging very\nhard when multiple HOCs are stacked.\n\nThe standard format is `WithXxx(OriginalName)`:\n\n```jsx\nfunction withSubscription(WrappedComponent) {\n  function WithSubscription(props) {\n    \u002F\u002F ...\n    return \u003CWrappedComponent {...props} \u002F>;\n  }\n\n  \u002F\u002F Correct format: WithHocName(WrappedName)\n  WithSubscription.displayName =\n    `WithSubscription(${\n      WrappedComponent.displayName  \u002F\u002F already set display name\n      ?? WrappedComponent.name      \u002F\u002F function\u002Fclass name\n      ?? 'Component'               \u002F\u002F last resort fallback\n    })`;\n\n  return WithSubscription;\n}\n\n\u002F\u002F DevTools now shows: WithSubscription(Dashboard)\n\u002F\u002F instead of:         WithSubscription or Anonymous\n```\n\nSome teams use a `getDisplayName` helper to deduplicate the ternary chain across\nall HOCs in a codebase.\n\n**Rule of thumb:** Always set `displayName` using the `WithXxx(InnerName)` format;\ncheck all three sources (`displayName`, `name`, fallback) in that priority order.\n",{"id":93,"difficulty":57,"q":94,"a":95},"render-props-error-boundaries","Can you use render props with error boundaries? How?","Yes. An **error boundary** must be a class component (React's `componentDidCatch`\nAPI is not yet available as a hook), but you can expose its caught error via a\nrender prop so the caller controls the fallback UI — combining the two patterns.\n\n```jsx\nclass ErrorBoundary extends React.Component {\n  state = { error: null };\n\n  static getDerivedStateFromError(error) {\n    return { error };\n  }\n\n  componentDidCatch(error, info) {\n    reportError(error, info); \u002F\u002F side-effect: logging service\n  }\n\n  render() {\n    const { error } = this.state;\n    const { children, fallback } = this.props;\n\n    if (error) {\n      \u002F\u002F Delegate fallback UI to the caller via render prop\n      return typeof fallback === 'function'\n        ? fallback(error)       \u002F\u002F render prop form\n        : fallback ?? \u003Cp>Something went wrong.\u003C\u002Fp>;\n    }\n\n    return children;\n  }\n}\n\n\u002F\u002F Caller controls the fallback UI\n\u003CErrorBoundary fallback={err => \u003CErrorPage message={err.message} \u002F>}>\n  \u003CUserProfile userId={id} \u002F>\n\u003C\u002FErrorBoundary>\n```\n\nThis pattern is exactly how the popular `react-error-boundary` library works\ninternally (`renderError` \u002F `FallbackComponent` prop).\n\n**Rule of thumb:** Error boundary logic stays in a class component; expose the\nfallback slot as a render prop so each call site can render appropriate recovery UI.\n",{"id":97,"difficulty":57,"q":98,"a":99},"render-prop-pure-component-issue","Why can inline render prop functions cause issues with PureComponent or React.memo and how do you fix it?","`React.PureComponent` and `React.memo` perform a **shallow comparison** of props.\nAn inline arrow function creates a new function object on every parent render, so\nthe render-prop component always sees a changed `render` prop — the shallow compare\nfails, PureComponent's optimisation is bypassed, and the component re-renders\nunconditionally.\n\n```jsx\nclass Mouse extends React.PureComponent {\n  \u002F\u002F shallow compare on props.render — always a new reference → always re-renders\n  render() { return this.props.render(this.state); }\n}\n\n\u002F\u002F BAD — new arrow function on every render of App\nclass App extends React.Component {\n  render() {\n    return \u003CMouse render={pos => \u003CCat {...pos} \u002F>} \u002F>;  \u002F\u002F new fn each time!\n  }\n}\n\n\u002F\u002F GOOD — define the render function as an instance method; reference is stable\nclass App extends React.Component {\n  renderCat = pos => \u003CCat {...pos} \u002F>;  \u002F\u002F stable class field\n\n  render() {\n    return \u003CMouse render={this.renderCat} \u002F>;\n  }\n}\n\n\u002F\u002F GOOD (function component equivalent) — useCallback for stable reference\nfunction App() {\n  const renderCat = React.useCallback(pos => \u003CCat {...pos} \u002F>, []);\n  return \u003CMouse render={renderCat} \u002F>;\n}\n```\n\n**Rule of thumb:** Never pass an inline arrow function as a render prop to a\n`PureComponent` or `memo`-wrapped component; stabilise the reference with\n`useCallback` or a class instance method.\n",19,null,{"description":11},"React render props and HOC interview questions — function-as-child, higher-order components, withAuth, cross-cutting concerns, hooks vs HOCs comparison.","react\u002Fpatterns\u002Frender-props-hoc","Render Props & HOCs","Patterns","patterns","2026-06-24","5b8a_FFY9oOxE5j6YoohtNraMG4vZoSdafVNh58xkH8",[111,115,116,120,124],{"subtopic":112,"path":113,"order":114},"Compound Components","\u002Freact\u002Fpatterns\u002Fcompound-components",1,{"subtopic":105,"path":20,"order":12},{"subtopic":117,"path":118,"order":119},"Error Boundaries","\u002Freact\u002Fpatterns\u002Ferror-boundaries",3,{"subtopic":121,"path":122,"order":123},"Portals & Refs","\u002Freact\u002Fpatterns\u002Fportals-refs",4,{"subtopic":125,"path":126,"order":127},"forwardRef & useImperativeHandle","\u002Freact\u002Fpatterns\u002Fforward-ref-imperative",5,{"path":129,"title":130},"\u002Fblog\u002Freact-render-props-hoc-guide","React Render Props & HOCs — Complete Interview Guide",1782244101281]