Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions packages/router/src/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,17 @@ export function useParams(): RouteParams {
/**
* Returns a function to trigger navigation.
*/
export function useNavigate(): (path: string, options?: { replace?: boolean; query?: QueryParams }) => void {
export function useNavigate(): (path: string, options?: { replace?: boolean }) => void {
const router = useContext(RouterContext);

return (path: string, options?: { replace?: boolean; query?: QueryParams }) => {
return (path: string, options?: { replace?: boolean }) => {
if (!router) {
return;
}
if (options?.replace) {
router.replace(path, { query: options?.query });
router.replace(path);
} else {
router.push(path, { query: options?.query });
router.push(path);
}
};
}
Expand All @@ -53,5 +53,6 @@ export function useQueryParams(): QueryParams {
if (!router) {
return {};
}
return router.query;
// Query params are not currently supported by the Router
return {};
}
76 changes: 68 additions & 8 deletions packages/router/src/redirect.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ const DummyComponent = () => ({ type: 'box', props: {}, children: [] } as any);
describe('Router Redirects', () => {
it('handles static redirect', () => {
const router = new Router();
router.addRoute('/old', DummyComponent, undefined, undefined, undefined, '/new');
router.addRoute('/old', DummyComponent, undefined, {
beforeEnter: () => '/new',
});
router.addRoute('/new', DummyComponent);

router.push('/old');
Expand All @@ -15,7 +17,12 @@ describe('Router Redirects', () => {

it('handles function redirect', () => {
const router = new Router();
router.addRoute('/user/[id]', DummyComponent, undefined, undefined, undefined, (params) => `/profile/${params.id}`);
router.addRoute('/user/[id]', DummyComponent, undefined, {
beforeEnter: (to) => {
const match = to.match(/\/user\/(.+)/);
return match ? `/profile/${match[1]}` : to;
},
});
router.addRoute('/profile/[id]', DummyComponent);

router.push('/user/123');
Expand All @@ -26,7 +33,9 @@ describe('Router Redirects', () => {
it('handles replace redirect', () => {
const router = new Router();
router.addRoute('/start', DummyComponent);
router.addRoute('/old', DummyComponent, undefined, undefined, undefined, '/new');
router.addRoute('/old', DummyComponent, undefined, {
beforeEnter: () => '/new',
});
router.addRoute('/new', DummyComponent);

router.push('/start');
Expand All @@ -37,8 +46,12 @@ describe('Router Redirects', () => {

it('handles redirect chain', () => {
const router = new Router();
router.addRoute('/a', DummyComponent, undefined, undefined, undefined, '/b');
router.addRoute('/b', DummyComponent, undefined, undefined, undefined, '/c');
router.addRoute('/a', DummyComponent, undefined, {
beforeEnter: () => '/b',
});
router.addRoute('/b', DummyComponent, undefined, {
beforeEnter: () => '/c',
});
router.addRoute('/c', DummyComponent);

router.push('/a');
Expand All @@ -50,12 +63,59 @@ describe('Router Redirects', () => {
const errorHandler = vi.fn();
router.events.on('error', errorHandler);

router.addRoute('/a', DummyComponent, undefined, undefined, undefined, '/b');
router.addRoute('/b', DummyComponent, undefined, undefined, undefined, '/a');
router.addRoute('/a', DummyComponent, undefined, {
beforeEnter: () => '/b',
});
router.addRoute('/b', DummyComponent, undefined, {
beforeEnter: () => '/a',
});

router.push('/a');

expect(errorHandler).toHaveBeenCalled();
expect(errorHandler.mock.calls[0][0].message).toMatch(/Max redirect depth exceeded/);
expect(errorHandler.mock.calls[0][0].message).toMatch(/Too many redirects/);
});

it('detects cyclic redirect in replace and emits error', () => {
const router = new Router();
const errorHandler = vi.fn();
router.events.on('error', errorHandler);

router.addRoute('/start', DummyComponent);
router.addRoute('/a', DummyComponent, undefined, {
beforeEnter: () => '/b',
});
router.addRoute('/b', DummyComponent, undefined, {
beforeEnter: () => '/a',
});

router.push('/start');
router.replace('/a');

expect(errorHandler).toHaveBeenCalled();
expect(errorHandler.mock.calls[0][0].message).toMatch(/Too many redirects/);
});

it('resets redirect depth on successful navigation', () => {
const router = new Router();
const errorHandler = vi.fn();
router.events.on('error', errorHandler);

router.addRoute('/a', DummyComponent, undefined, {
beforeEnter: () => '/b',
});
router.addRoute('/b', DummyComponent, undefined, {
beforeEnter: () => '/c',
});
router.addRoute('/c', DummyComponent);

router.push('/a');
expect(router.currentPath).toBe('/c');
expect(errorHandler).not.toHaveBeenCalled();

// Navigate again should reset depth
router.push('/a');
expect(router.currentPath).toBe('/c');
expect(errorHandler).not.toHaveBeenCalled();
});
});
21 changes: 0 additions & 21 deletions packages/router/src/router.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,27 +194,6 @@ describe('Router', () => {
expect(r.currentPath).toBe('/login');
});

it('back() with beforeEnter redirect does not corrupt history', () => {
const r = new Router();
r.addRoute('/login', () => 'Login');
r.addRoute('/dashboard', () => 'Dashboard', undefined, {
beforeEnter: () => '/login',
});
r.addRoute('/settings', () => 'Settings');

r.push('/login');
r.push('/dashboard');
r.push('/settings');

expect(r.currentPath).toBe('/settings');
expect(r.historyLength).toBe(3);

r.back();

expect(r.currentPath).toBe('/login');
expect(r.historyLength).toBe(2);
});

it('afterEnter executes after navigation', () => {
const r = new Router();
const spy = vi.fn();
Expand Down
Loading
Loading