aboutsummaryrefslogtreecommitdiffstats
path: root/src/utils/hooks/use-on-route-change
diff options
context:
space:
mode:
Diffstat (limited to 'src/utils/hooks/use-on-route-change')
-rw-r--r--src/utils/hooks/use-on-route-change/index.ts1
-rw-r--r--src/utils/hooks/use-on-route-change/use-on-route-change.test.tsx69
-rw-r--r--src/utils/hooks/use-on-route-change/use-on-route-change.ts35
3 files changed, 105 insertions, 0 deletions
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]);
+};