[{"data":1,"prerenderedAt":102},["ShallowReactive",2],{"qa-\u002Freact\u002Ftesting\u002Ftesting-custom-hooks":3},{"page":4,"siblings":86,"blog":99},{"id":5,"title":6,"body":7,"description":11,"difficulty":14,"extension":15,"framework":16,"frameworkSlug":17,"meta":18,"navigation":19,"order":20,"path":21,"questions":22,"questionsCount":77,"related":78,"seo":79,"seoDescription":80,"stem":81,"subtopic":6,"topic":82,"topicSlug":83,"updated":84,"__hash__":85},"qa\u002Freact\u002Ftesting\u002Ftesting-custom-hooks.md","Testing Custom Hooks",{"type":8,"value":9,"toc":10},"minimark",[],{"title":11,"searchDepth":12,"depth":12,"links":13},"",2,[],"hard","md","React","react",{},true,4,"\u002Freact\u002Ftesting\u002Ftesting-custom-hooks",[23,28,32,36,40,44,48,52,57,61,65,69,73],{"id":24,"difficulty":25,"q":26,"a":27},"render-hook-intro","medium","What is `renderHook` and how does it work?","`renderHook` from `@testing-library\u002Freact` mounts a minimal wrapper\ncomponent that calls the hook under test and exposes its return value\nthrough a `result` object. It is the standard way to test hooks in\nisolation without building a throwaway UI component.\n\n```js\nimport { renderHook } from '@testing-library\u002Freact'\nimport { useCounter } from '.\u002FuseCounter'\n\ntest('initializes with given value', () => {\n  const { result } = renderHook(() => useCounter(10))\n\n  \u002F\u002F result.current holds whatever the hook returns\n  expect(result.current.count).toBe(10)\n  expect(typeof result.current.increment).toBe('function')\n})\n```\n\nThe wrapper component re-renders each time the hook causes a state\nupdate, and `result.current` always reflects the *latest* return value.\n\nInternally, `renderHook(callback)` is roughly:\n```js\nfunction TestHook() {\n  result.current = callback()  \u002F\u002F captures hook return value\n  return null\n}\nrender(\u003CTestHook \u002F>)\n```\n\nUpdating arguments on re-render:\n```js\nconst { result, rerender } = renderHook(\n  ({ step }) => useCounter(0, step),\n  { initialProps: { step: 1 } }\n)\nrerender({ step: 5 })   \u002F\u002F hook re-executes with new props\n```\n\n**Rule of thumb:** Use `renderHook` when the hook encapsulates logic\nyou want to verify independently; use component tests when the\nhook is simple and fully covered by the component's test.\n",{"id":29,"difficulty":25,"q":30,"a":31},"act-in-hook-tests","Why do you need to wrap hook state updates in `act()`?","React batches state updates and applies them asynchronously in its\nfiber scheduler. `act()` tells React's test utilities: \"all state changes\ninside this block should be flushed before assertions run.\" Without it\nyou may assert before React has committed the updated state.\n\n```js\nimport { renderHook, act } from '@testing-library\u002Freact'\nimport { useToggle } from '.\u002FuseToggle'\n\ntest('toggles between true and false', () => {\n  const { result } = renderHook(() => useToggle(false))\n\n  expect(result.current.isOn).toBe(false)\n\n  \u002F\u002F Wrap the state-updating call in act()\n  act(() => {\n    result.current.toggle()\n  })\n\n  expect(result.current.isOn).toBe(true)\n\n  act(() => {\n    result.current.toggle()\n  })\n\n  expect(result.current.isOn).toBe(false)\n})\n```\n\nFor async updates (hooks that call `setState` inside a Promise or timer):\n```js\nawait act(async () => {\n  result.current.fetchData()\n})\n\u002F\u002F Safe to assert here — all async state changes have been flushed\n```\n\nRTL's `userEvent` helpers wrap their actions in `act` automatically, which\nis one reason to prefer `userEvent` over manual `act` calls in component\ntests. For hook tests you usually call `act` directly.\n\n**Rule of thumb:** Any call inside `renderHook` that triggers a state\nupdate must be wrapped in `act()`. If you see the \"not wrapped in act\"\nwarning, find the state update and wrap the trigger.\n",{"id":33,"difficulty":14,"q":34,"a":35},"async-hook-test","How do you test a custom hook that fetches data asynchronously?","Combine `renderHook`, `waitFor`, and a mock (MSW or `vi.fn()`) to assert\non loading, success, and error states.\n\n```js\nimport { renderHook, waitFor } from '@testing-library\u002Freact'\nimport { server } from '..\u002Fmocks\u002Fserver'\nimport { http, HttpResponse } from 'msw'\nimport { useFetchUser } from '.\u002FuseFetchUser'\n\n\u002F\u002F useFetchUser: returns { user, loading, error }\n\ntest('transitions loading → data', async () => {\n  server.use(\n    http.get('\u002Fapi\u002Fusers\u002F1', () =>\n      HttpResponse.json({ id: 1, name: 'Alice' })\n    )\n  )\n\n  const { result } = renderHook(() => useFetchUser(1))\n\n  \u002F\u002F Loading state immediately after mount\n  expect(result.current.loading).toBe(true)\n  expect(result.current.user).toBeNull()\n\n  \u002F\u002F Wait until loading is false\n  await waitFor(() => expect(result.current.loading).toBe(false))\n\n  expect(result.current.user).toEqual({ id: 1, name: 'Alice' })\n  expect(result.current.error).toBeNull()\n})\n\ntest('sets error on fetch failure', async () => {\n  server.use(\n    http.get('\u002Fapi\u002Fusers\u002F1', () => new HttpResponse(null, { status: 500 }))\n  )\n\n  const { result } = renderHook(() => useFetchUser(1))\n\n  await waitFor(() => expect(result.current.loading).toBe(false))\n\n  expect(result.current.error).toBeTruthy()\n  expect(result.current.user).toBeNull()\n})\n```\n\n**Rule of thumb:** Use `waitFor(() => expect(...))` rather than\n`findBy*` in hook tests because hooks return values, not DOM elements.\n",{"id":37,"difficulty":25,"q":38,"a":39},"context-hook-test","How do you test a hook that depends on React context?","Pass a `wrapper` option to `renderHook` that wraps the hook in the\nrequired provider, the same way you'd wrap a component.\n\n```js\nimport { renderHook } from '@testing-library\u002Freact'\nimport { AuthProvider } from '.\u002FAuthContext'\nimport { useAuth } from '.\u002FuseAuth'\n\nfunction createWrapper(user) {\n  return function AuthWrapper({ children }) {\n    return \u003CAuthProvider initialUser={user}>{children}\u003C\u002FAuthProvider>\n  }\n}\n\ntest('returns current user from context', () => {\n  const { result } = renderHook(() => useAuth(), {\n    wrapper: createWrapper({ id: 1, name: 'Alice', role: 'admin' }),\n  })\n\n  expect(result.current.user).toEqual({ id: 1, name: 'Alice', role: 'admin' })\n  expect(result.current.isAuthenticated).toBe(true)\n})\n\ntest('returns null user when not authenticated', () => {\n  const { result } = renderHook(() => useAuth(), {\n    wrapper: createWrapper(null),\n  })\n\n  expect(result.current.user).toBeNull()\n  expect(result.current.isAuthenticated).toBe(false)\n})\n\ntest('throws when used outside AuthProvider', () => {\n  \u002F\u002F No wrapper — hook should throw\n  expect(() => renderHook(() => useAuth())).toThrow(\u002Fmust be used within AuthProvider\u002Fi)\n})\n```\n\n**Rule of thumb:** Always test the \"missing provider\" error path for\nhooks that call `useContext` — it verifies your guard condition and\nproduces a clear error message in production.\n",{"id":41,"difficulty":25,"q":42,"a":43},"useeffect-hook-test","How do you test the side effects of a custom hook that uses `useEffect`?","Wrap the side-effectful action in `act()` and assert the result after\n`act` resolves. For side effects that happen on mount, they run\nsynchronously during `renderHook` (within RTL's own `act` call).\n\n```js\nimport { renderHook, act } from '@testing-library\u002Freact'\nimport { useDocumentTitle } from '.\u002FuseDocumentTitle'\n\n\u002F\u002F useDocumentTitle: sets document.title to the given string\ntest('sets document title on mount', () => {\n  renderHook(() => useDocumentTitle('My Page'))\n  expect(document.title).toBe('My Page')\n})\n\ntest('updates document title when value changes', () => {\n  const { rerender } = renderHook(\n    ({ title }) => useDocumentTitle(title),\n    { initialProps: { title: 'Page 1' } }\n  )\n\n  expect(document.title).toBe('Page 1')\n\n  rerender({ title: 'Page 2' })\n  expect(document.title).toBe('Page 2')\n})\n\ntest('restores title on unmount', () => {\n  document.title = 'Original'\n  const { unmount } = renderHook(() => useDocumentTitle('Temp'))\n  expect(document.title).toBe('Temp')\n  unmount()\n  expect(document.title).toBe('Original')\n})\n```\n\nFor async effects (data fetching on mount), use `waitFor` as shown in\nthe `useFetchUser` example.\n\n**Rule of thumb:** Test three lifecycle moments: initial mount effect,\nupdate-triggered effect, and cleanup on unmount.\n",{"id":45,"difficulty":14,"q":46,"a":47},"timer-hook-test","How do you test a custom hook that internally uses `setTimeout` or `setInterval`?","Use `vi.useFakeTimers()` and advance time inside `act()`.\n\n```js\nimport { renderHook, act } from '@testing-library\u002Freact'\nimport { vi } from 'vitest'\nimport { useDebounce } from '.\u002FuseDebounce'\n\ndescribe('useDebounce', () => {\n  beforeEach(() => vi.useFakeTimers())\n  afterEach(() => vi.useRealTimers())\n\n  test('returns initial value immediately', () => {\n    const { result } = renderHook(() => useDebounce('hello', 300))\n    expect(result.current).toBe('hello')\n  })\n\n  test('does not update before delay', () => {\n    const { result, rerender } = renderHook(\n      ({ value }) => useDebounce(value, 300),\n      { initialProps: { value: 'hello' } }\n    )\n\n    rerender({ value: 'world' })\n    act(() => vi.advanceTimersByTime(100))\n    expect(result.current).toBe('hello')   \u002F\u002F not yet updated\n  })\n\n  test('updates after debounce delay', () => {\n    const { result, rerender } = renderHook(\n      ({ value }) => useDebounce(value, 300),\n      { initialProps: { value: 'hello' } }\n    )\n\n    rerender({ value: 'world' })\n    act(() => vi.advanceTimersByTime(300))\n    expect(result.current).toBe('world')\n  })\n\n  test('clears pending timer on unmount', () => {\n    const clearTimeoutSpy = vi.spyOn(global, 'clearTimeout')\n    const { unmount } = renderHook(() => useDebounce('test', 300))\n    unmount()\n    expect(clearTimeoutSpy).toHaveBeenCalled()\n  })\n})\n```\n\n**Rule of thumb:** Always test the cleanup path (`unmount`) for timer\nhooks — leaking timers is a common source of \"act warning\" failures in\nsubsequent tests.\n",{"id":49,"difficulty":25,"q":50,"a":51},"reducer-hook-test","How do you test a hook built on `useReducer`?","Test through the hook's public interface (the actions\u002Fdispatch wrappers\nit returns), not by inspecting the reducer directly. The reducer itself\ncan have its own pure unit tests.\n\n```js\nimport { renderHook, act } from '@testing-library\u002Freact'\nimport { useShoppingCart } from '.\u002FuseShoppingCart'\n\ntest('adds and removes items', () => {\n  const { result } = renderHook(() => useShoppingCart())\n\n  expect(result.current.items).toHaveLength(0)\n  expect(result.current.total).toBe(0)\n\n  act(() => {\n    result.current.addItem({ id: 1, name: 'Widget', price: 9.99 })\n  })\n  expect(result.current.items).toHaveLength(1)\n  expect(result.current.total).toBeCloseTo(9.99)\n\n  act(() => {\n    result.current.addItem({ id: 2, name: 'Gadget', price: 24.99 })\n  })\n  expect(result.current.total).toBeCloseTo(34.98)\n\n  act(() => {\n    result.current.removeItem(1)\n  })\n  expect(result.current.items).toHaveLength(1)\n  expect(result.current.total).toBeCloseTo(24.99)\n})\n\ntest('clears cart', () => {\n  const { result } = renderHook(() => useShoppingCart())\n  act(() => {\n    result.current.addItem({ id: 1, name: 'Widget', price: 9.99 })\n    result.current.clearCart()\n  })\n  expect(result.current.items).toHaveLength(0)\n})\n```\n\nSeparately, test the reducer as a pure function:\n```js\nimport { cartReducer } from '.\u002FcartReducer'\n\ntest('ADD_ITEM increases quantity for existing item', () => {\n  const state = { items: [{ id: 1, qty: 1, price: 10 }] }\n  const next = cartReducer(state, { type: 'ADD_ITEM', payload: { id: 1 } })\n  expect(next.items[0].qty).toBe(2)\n})\n```\n\n**Rule of thumb:** Test hooks through their action API (what the caller\nsees); test reducers as pure functions (same input → same output).\n",{"id":53,"difficulty":54,"q":55,"a":56},"hook-args-test","easy","How do you test a hook that accepts arguments, and test how it behaves when those arguments change?","Pass `initialProps` to `renderHook` and call `rerender` with new props\nto simulate prop changes.\n\n```js\nimport { renderHook } from '@testing-library\u002Freact'\nimport { usePagination } from '.\u002FusePagination'\n\ntest('initializes with given page and pageSize', () => {\n  const { result } = renderHook(\n    ({ page, pageSize }) => usePagination({ page, pageSize, total: 100 }),\n    { initialProps: { page: 1, pageSize: 10 } }\n  )\n\n  expect(result.current.currentPage).toBe(1)\n  expect(result.current.totalPages).toBe(10)\n  expect(result.current.startIndex).toBe(0)\n  expect(result.current.endIndex).toBe(9)\n})\n\ntest('recalculates when pageSize changes', () => {\n  const { result, rerender } = renderHook(\n    ({ pageSize }) => usePagination({ page: 1, pageSize, total: 100 }),\n    { initialProps: { pageSize: 10 } }\n  )\n\n  expect(result.current.totalPages).toBe(10)\n\n  rerender({ pageSize: 20 })\n  expect(result.current.totalPages).toBe(5)\n})\n\ntest('clamps page when it exceeds new totalPages', () => {\n  const { result, rerender } = renderHook(\n    ({ total }) => usePagination({ page: 5, pageSize: 10, total }),\n    { initialProps: { total: 100 } }\n  )\n\n  expect(result.current.currentPage).toBe(5)\n\n  rerender({ total: 30 })    \u002F\u002F now only 3 pages\n  expect(result.current.currentPage).toBe(3)  \u002F\u002F clamped\n})\n```\n\n**Rule of thumb:** Use `initialProps` + `rerender` to test argument\nchanges; this is the hook equivalent of testing prop changes via\n`render` + `rerender`.\n",{"id":58,"difficulty":25,"q":59,"a":60},"hook-vs-component-test","When should you test a custom hook directly with `renderHook` versus testing it through the component that uses it?","Both are valid — the choice depends on the complexity and reuse of the\nhook.\n\n**Test through the component when:**\n- The hook is tightly coupled to one component and used nowhere else.\n- The hook's behavior is fully visible through the component's UI.\n- The hook is simple (one or two state variables).\n\n```js\n\u002F\u002F useExpanded is used only in Accordion — test through Accordion\ntest('expands panel on click', async () => {\n  const user = userEvent.setup()\n  render(\u003CAccordion panels={[{ title: 'Q1', body: 'A1' }]} \u002F>)\n  expect(screen.queryByText('A1')).not.toBeInTheDocument()\n  await user.click(screen.getByRole('button', { name: \u002FQ1\u002Fi }))\n  expect(screen.getByText('A1')).toBeInTheDocument()\n})\n```\n\n**Test the hook directly when:**\n- The hook encapsulates complex logic used by multiple components.\n- The hook has many edge cases that would be tedious to exercise via UI.\n- The hook manages async state (loading\u002Ferror\u002Fdata transitions).\n- You want to document the hook's API contract separately.\n\n```js\n\u002F\u002F useInfiniteScroll is reused across 4 components — test directly\ntest('loads next page when sentinel is visible', async () => {\n  const { result } = renderHook(() => useInfiniteScroll(fetchPage))\n  \u002F\u002F ... assert loading, page count, hasMore\n})\n```\n\nA practical middle ground: write one integration test per major use-case\nat the component level, then add direct hook tests for tricky edge cases.\n\n**Rule of thumb:** Start with component tests. Add direct hook tests\nwhen the component test becomes unwieldy or when the hook is shared.\n",{"id":62,"difficulty":14,"q":63,"a":64},"mock-hook-dependency","How do you mock a module dependency used inside a custom hook?","Use `vi.mock()` to replace the dependency before the hook (and any\ncomponent using it) imports it.\n\n```js\nimport { renderHook } from '@testing-library\u002Freact'\nimport { vi } from 'vitest'\nimport * as analyticsModule from '..\u002Futils\u002Fanalytics'\nimport { useProductView } from '.\u002FuseProductView'\n\nvi.mock('..\u002Futils\u002Fanalytics')\n\nconst mockedAnalytics = vi.mocked(analyticsModule)\n\nbeforeEach(() => {\n  mockedAnalytics.trackEvent.mockImplementation(() => {})\n})\n\nafterEach(() => vi.clearAllMocks())\n\ntest('tracks product view event on mount', () => {\n  renderHook(() => useProductView({ id: 42, name: 'Widget' }))\n\n  expect(mockedAnalytics.trackEvent).toHaveBeenCalledWith('product_view', {\n    product_id: 42,\n    product_name: 'Widget',\n  })\n})\n\ntest('tracks only once even if hook re-renders', () => {\n  const { rerender } = renderHook(() => useProductView({ id: 42, name: 'Widget' }))\n  rerender()\n  rerender()\n  expect(mockedAnalytics.trackEvent).toHaveBeenCalledTimes(1)\n})\n```\n\nFor mocking a hook that's used inside another hook (hook composition):\n```js\nvi.mock('.\u002FuseAuth', () => ({\n  useAuth: () => ({ user: { id: 1 }, isAuthenticated: true }),\n}))\n```\n\n**Rule of thumb:** Mock at the module boundary, not deep inside the\nimplementation. If you're mocking an internal helper, the hook's\nresponsibility boundary may be too large.\n",{"id":66,"difficulty":14,"q":67,"a":68},"ref-hook-test","How do you test a custom hook that uses `useRef` to interact with DOM elements?","`renderHook` by itself doesn't produce DOM elements — you need a component\nto attach refs to real DOM nodes. Use a custom wrapper component.\n\n```js\nimport { render, screen, act } from '@testing-library\u002Freact'\nimport userEvent from '@testing-library\u002Fuser-event'\nimport { useAutoFocus } from '.\u002FuseAutoFocus'\n\n\u002F\u002F useAutoFocus: takes a ref and calls .focus() on mount\n\ntest('focuses element on mount', () => {\n  function TestComponent() {\n    const ref = React.useRef(null)\n    useAutoFocus(ref)\n    return \u003Cinput ref={ref} aria-label=\"Auto-focused input\" \u002F>\n  }\n\n  render(\u003CTestComponent \u002F>)\n  expect(screen.getByRole('textbox', { name: \u002Fauto-focused\u002Fi })).toHaveFocus()\n})\n```\n\nFor hooks that expose a ref (e.g., `useScrollLock`):\n```js\nfunction TestComponent() {\n  const containerRef = React.useRef(null)\n  const { lock, unlock } = useScrollLock(containerRef)\n  return (\n    \u003Cdiv ref={containerRef} style={{ overflow: 'auto' }}>\n      \u003Cbutton onClick={lock}>Lock\u003C\u002Fbutton>\n      \u003Cbutton onClick={unlock}>Unlock\u003C\u002Fbutton>\n    \u003C\u002Fdiv>\n  )\n}\n\ntest('sets overflow hidden on lock', async () => {\n  const user = userEvent.setup()\n  render(\u003CTestComponent \u002F>)\n  await user.click(screen.getByRole('button', { name: \u002Flock\u002Fi }))\n  \u002F\u002F Assert DOM side-effect\n  expect(screen.getByRole('button', { name: \u002Flock\u002Fi }).closest('div'))\n    .toHaveStyle({ overflow: 'hidden' })\n})\n```\n\n**Rule of thumb:** When a hook needs a real DOM node (for refs,\nresize observers, intersection observers), write a small wrapper\ncomponent in the test file rather than forcing `renderHook` to do it.\n",{"id":70,"difficulty":25,"q":71,"a":72},"hook-cleanup-test","How do you verify that a custom hook properly cleans up its effects on unmount?","Call `unmount()` from the `renderHook` result, then assert that side\neffects have been reversed — event listeners removed, timers cleared,\nsubscriptions cancelled.\n\n```js\nimport { renderHook } from '@testing-library\u002Freact'\nimport { vi } from 'vitest'\nimport { useWindowResize } from '.\u002FuseWindowResize'\n\ntest('removes event listener on unmount', () => {\n  const addSpy = vi.spyOn(window, 'addEventListener')\n  const removeSpy = vi.spyOn(window, 'removeEventListener')\n\n  const { unmount } = renderHook(() => useWindowResize(() => {}))\n\n  expect(addSpy).toHaveBeenCalledWith('resize', expect.any(Function))\n\n  unmount()\n\n  \u002F\u002F Same handler that was added should be removed\n  expect(removeSpy).toHaveBeenCalledWith('resize', expect.any(Function))\n\n  addSpy.mockRestore()\n  removeSpy.mockRestore()\n})\n\ntest('cancels pending subscription on unmount', () => {\n  const unsubscribe = vi.fn()\n  const subscribe = vi.fn().mockReturnValue(unsubscribe)\n\n  const { unmount } = renderHook(() => useStoreSubscription(subscribe))\n\n  expect(subscribe).toHaveBeenCalledTimes(1)\n  unmount()\n  expect(unsubscribe).toHaveBeenCalledTimes(1)\n})\n```\n\nCleanup tests matter because leaking subscriptions or listeners cause:\n- Memory leaks in production.\n- \"Can't perform state update on unmounted component\" warnings.\n- State updates firing after the component is gone (act warnings in tests).\n\n**Rule of thumb:** Write an unmount cleanup test for every hook that\nregisters event listeners, starts timers, or opens subscriptions.\n",{"id":74,"difficulty":25,"q":75,"a":76},"hook-error-test","How do you test that a hook throws an error under invalid conditions?","Wrap the `renderHook` call in a `try\u002Fcatch` or use\n`expect(() => ...).toThrow()`. Because React catches errors during\nrender and logs them, suppress `console.error` too.\n\n```js\nimport { renderHook } from '@testing-library\u002Freact'\nimport { vi } from 'vitest'\nimport { useRequiredContext } from '.\u002FuseRequiredContext'\n\ntest('throws when used outside provider', () => {\n  const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {})\n\n  expect(() => renderHook(() => useRequiredContext())).toThrow(\n    'useRequiredContext must be used inside RequiredContextProvider'\n  )\n\n  consoleSpy.mockRestore()\n})\n```\n\nFor hooks that validate their arguments:\n```js\ntest('throws for negative page size', () => {\n  const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {})\n\n  expect(() =>\n    renderHook(() => usePagination({ page: 1, pageSize: -1, total: 100 }))\n  ).toThrow('pageSize must be positive')\n\n  consoleSpy.mockRestore()\n})\n```\n\nNote: React 18 with concurrent mode may not throw immediately on the\nfirst render — if you find that `expect(...).toThrow()` doesn't catch\nthe error, use an Error Boundary wrapper instead.\n\n**Rule of thumb:** Test the error guard condition (missing provider,\ninvalid args) so users get a clear message in development. Suppress\n`console.error` in these tests to keep output clean.\n",13,null,{"description":11},"Testing custom React hooks interview questions — renderHook, act, async hooks, context-dependent hooks, hook cleanup, refs, when to test hooks directly vs through components, and mocking hook dependencies.","react\u002Ftesting\u002Ftesting-custom-hooks","Testing","testing","2026-06-24","e-_K5wqYhGwL-YnIKLEUSaPIEC5MDbvOACpbC3E5Qr4",[87,91,94,98],{"subtopic":88,"path":89,"order":90},"RTL Basics","\u002Freact\u002Ftesting\u002Frtl-basics",1,{"subtopic":92,"path":93,"order":12},"Component Interaction Testing","\u002Freact\u002Ftesting\u002Fcomponent-interaction-testing",{"subtopic":95,"path":96,"order":97},"Mocking Async","\u002Freact\u002Ftesting\u002Fmocking-async",3,{"subtopic":6,"path":21,"order":20},{"path":100,"title":101},"\u002Fblog\u002Freact-testing-custom-hooks-guide","Testing React Custom Hooks — Complete Guide",1782244101451]