diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/components/templates/layout/layout.tsx | 6 | ||||
| -rw-r--r-- | src/utils/hooks/index.ts | 2 | ||||
| -rw-r--r-- | src/utils/hooks/use-on-route-change/index.ts | 1 | ||||
| -rw-r--r-- | src/utils/hooks/use-on-route-change/use-on-route-change.test.tsx | 69 | ||||
| -rw-r--r-- | src/utils/hooks/use-on-route-change/use-on-route-change.ts | 35 | ||||
| -rw-r--r-- | src/utils/hooks/use-route-change.tsx | 10 | 
6 files changed, 109 insertions, 14 deletions
| diff --git a/src/components/templates/layout/layout.tsx b/src/components/templates/layout/layout.tsx index 5dad8a2..055b1a1 100644 --- a/src/components/templates/layout/layout.tsx +++ b/src/components/templates/layout/layout.tsx @@ -19,7 +19,7 @@ import { ROUTES } from '../../../utils/constants';  import {    useAutofocus,    useBoolean, -  useRouteChange, +  useOnRouteChange,    useScrollPosition,  } from '../../../utils/hooks';  import { @@ -271,7 +271,7 @@ export const Layout: FC<LayoutProps> = ({      [intl, router]    ); -  useRouteChange(deactivateSearch); +  useOnRouteChange(deactivateSearch);    const navbarItems: NavbarItems = [      { @@ -382,7 +382,7 @@ export const Layout: FC<LayoutProps> = ({      if (topRef.current) topRef.current.focus();    }; -  useRouteChange(giveFocusToTopRef); +  useOnRouteChange(giveFocusToTopRef);    const brandingTitleStyles = {      '--typing-animation': diff --git a/src/utils/hooks/index.ts b/src/utils/hooks/index.ts index ad412c8..d42c32b 100644 --- a/src/utils/hooks/index.ts +++ b/src/utils/hooks/index.ts @@ -13,6 +13,7 @@ export * from './use-local-storage';  export * from './use-match-media';  export * from './use-mutation-observer';  export * from './use-on-click-outside'; +export * from './use-on-route-change';  export * from './use-pagination';  export * from './use-posts-list';  export * from './use-prism'; @@ -20,7 +21,6 @@ export * from './use-prism-theme';  export * from './use-reading-time';  export * from './use-redirection';  export * from './use-reduced-motion'; -export * from './use-route-change';  export * from './use-scroll-lock';  export * from './use-scroll-position';  export * from './use-scrollbar-width'; diff --git a/src/utils/hooks/use-on-route-change/index.ts b/src/utils/hooks/use-on-route-change/index.ts new file mode 100644 index 0000000..d7f54c8 --- /dev/null +++ b/src/utils/hooks/use-on-route-change/index.ts @@ -0,0 +1 @@ +export * from './use-on-route-change'; diff --git a/src/utils/hooks/use-on-route-change/use-on-route-change.test.tsx b/src/utils/hooks/use-on-route-change/use-on-route-change.test.tsx new file mode 100644 index 0000000..afcad0a --- /dev/null +++ b/src/utils/hooks/use-on-route-change/use-on-route-change.test.tsx @@ -0,0 +1,69 @@ +import { describe, it } from '@jest/globals'; +import { render, screen as rtlScreen } from '@testing-library/react'; +import { userEvent } from '@testing-library/user-event'; +import Link from 'next/link'; +import nextRouterMock from 'next-router-mock'; +import { MemoryRouterProvider } from 'next-router-mock/MemoryRouterProvider'; +import { +  type OnRouteChangeHandler, +  useOnRouteChange, +  type OnRouteChangeStep, +} from './use-on-route-change'; + +type TestComponentProps = { +  callback: OnRouteChangeHandler; +  href: string; +  step?: OnRouteChangeStep; +}; + +const TestComponent = ({ callback, href, step }: TestComponentProps) => { +  useOnRouteChange(callback, step); + +  return ( +    <MemoryRouterProvider> +      <Link href={href}>New page</Link> +    </MemoryRouterProvider> +  ); +}; + +describe('useOnRouteChange', () => { +  it('trigger a callback when the route change event starts', async () => { +    const cb = jest.fn(); +    const user = userEvent.setup(); +    const newPage = '/new-page'; + +    nextRouterMock.push('/initial-page'); + +    render(<TestComponent callback={cb} href={newPage} />); + +    expect(cb).not.toHaveBeenCalled(); + +    await user.click(rtlScreen.getByRole('link')); + +    expect(nextRouterMock).toMatchObject({ +      asPath: newPage, +      pathname: newPage, +    }); +    expect(cb).toHaveBeenCalled(); +  }); + +  it('can trigger a callback when the route change event is complete', async () => { +    const cb = jest.fn(); +    const user = userEvent.setup(); +    const newPage = '/new-page'; + +    nextRouterMock.push('/initial-page'); + +    render(<TestComponent callback={cb} href={newPage} step="end" />); + +    expect(cb).not.toHaveBeenCalled(); + +    await user.click(rtlScreen.getByRole('link')); + +    expect(nextRouterMock).toMatchObject({ +      asPath: newPage, +      pathname: newPage, +    }); +    expect(cb).toHaveBeenCalled(); +  }); +}); diff --git a/src/utils/hooks/use-on-route-change/use-on-route-change.ts b/src/utils/hooks/use-on-route-change/use-on-route-change.ts new file mode 100644 index 0000000..85e7060 --- /dev/null +++ b/src/utils/hooks/use-on-route-change/use-on-route-change.ts @@ -0,0 +1,35 @@ +import { useRouter } from 'next/router'; +import { useEffect } from 'react'; + +export type OnRouteChangeStep = 'start' | 'end'; + +export type OnRouteChangeHandler = () => void; + +/** + * React hook to trigger a callback function on route change. + * + * @param {OnRouteChangeHandler} handler - The callback to trigger. + * @param {OnRouteChangeStep} [step] - The event step. + */ +export const useOnRouteChange = ( +  handler: OnRouteChangeHandler, +  step: OnRouteChangeStep = 'start' +) => { +  const router = useRouter(); + +  useEffect(() => { +    if (step === 'end') { +      router.events.on('routeChangeComplete', handler); + +      return () => { +        router.events.off('routeChangeComplete', handler); +      }; +    } + +    router.events.on('routeChangeStart', handler); + +    return () => { +      router.events.off('routeChangeStart', handler); +    }; +  }, [handler, router.events, step]); +}; diff --git a/src/utils/hooks/use-route-change.tsx b/src/utils/hooks/use-route-change.tsx deleted file mode 100644 index 2eff6e9..0000000 --- a/src/utils/hooks/use-route-change.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { useRouter } from 'next/router'; -import { useEffect } from 'react'; - -export const useRouteChange = (callback: () => void) => { -  const { events } = useRouter(); - -  useEffect(() => { -    events.on('routeChangeStart', callback); -  }, [events, callback]); -}; | 
