Skip to content

Commit 311f1f3

Browse files
fix(useIsActive): Support relative state names
1 parent d14075d commit 311f1f3

File tree

2 files changed

+37
-7
lines changed

2 files changed

+37
-7
lines changed

src/hooks/__tests__/useIsActive.test.tsx

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as React from 'react';
22
import { RawParams } from '@uirouter/core';
33
import { makeTestRouter } from '../../__tests__/util';
4+
import { UIView } from '../../components';
45
import { useIsActive } from '../useIsActive';
56

67
const state1 = { name: 'state1', url: '/state1' };
@@ -52,6 +53,24 @@ describe('useIsActive', () => {
5253
expect(wrapper.find('div').props().className).toBe('notactive');
5354
});
5455

56+
it('works with relative states', async () => {
57+
const parent = { name: 'parent', component: () => <TestComponent state=".child" params={null} exact={false} /> };
58+
const child = { name: 'parent.child', component: () => <div /> };
59+
router.stateRegistry.register(parent);
60+
router.stateRegistry.register(child);
61+
await routerGo('parent');
62+
const wrapper = mountInRouter(<UIView />);
63+
expect(wrapper.find('div').props().className).toBe('notactive');
64+
65+
await routerGo('parent.child');
66+
expect(
67+
wrapper
68+
.update()
69+
.find('div')
70+
.props().className
71+
).toBe('yesactive');
72+
});
73+
5574
it('updates when the desired state changes', async () => {
5675
await routerGo('state2');
5776
const wrapper = mountInRouter(<TestComponent state="state1" params={null} exact={false} />);

src/hooks/useIsActive.ts

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,39 @@
1-
import { StateService } from '@uirouter/core';
1+
import { StateDeclaration } from '@uirouter/core';
22
import { useEffect, useMemo, useState } from 'react';
3+
import { UIRouterReact } from '../core';
34
import { useDeepObjectDiff } from './useDeepObjectDiff';
45
import { useOnStateChanged } from './useOnStateChanged';
56
import { useRouter } from './useRouter';
7+
import { useViewContextState } from './useViewContextState';
68

7-
function checkIfActive(stateService: StateService, stateName: string, params: object, exact: boolean) {
8-
return exact ? stateService.is(stateName, params) : stateService.includes(stateName, params);
9+
function checkIfActive(
10+
router: UIRouterReact,
11+
stateName: string,
12+
params: object,
13+
relative: StateDeclaration,
14+
exact: boolean
15+
) {
16+
return exact
17+
? router.stateService.is(stateName, params, { relative })
18+
: router.stateService.includes(stateName, params, { relative });
919
}
1020

1121
export function useIsActive(stateName: string, params = null, exact = false) {
12-
const { stateService } = useRouter();
22+
const router = useRouter();
23+
const relative = useViewContextState(router);
1324
// Don't re-compute initialIsActive on every render
14-
const initialIsActive = useMemo(() => checkIfActive(stateService, stateName, params, exact), []);
25+
const initialIsActive = useMemo(() => checkIfActive(router, stateName, params, relative, exact), []);
1526
const [isActive, setIsActive] = useState(initialIsActive);
1627

1728
const checkIfActiveChanged = () => {
18-
const newIsActive = checkIfActive(stateService, stateName, params, exact);
29+
const newIsActive = checkIfActive(router, stateName, params, relative, exact);
1930
if (newIsActive !== isActive) {
2031
setIsActive(newIsActive);
2132
}
2233
};
2334

2435
useOnStateChanged(checkIfActiveChanged);
25-
useEffect(checkIfActiveChanged, [stateService, stateName, useDeepObjectDiff(params), exact]);
36+
useEffect(checkIfActiveChanged, [router, stateName, useDeepObjectDiff(params), exact]);
2637

2738
return isActive;
2839
}

0 commit comments

Comments
 (0)