Skip to content

Commit 30771a8

Browse files
authored
feat(selector): support dependencies (#32)
1 parent f3d211a commit 30771a8

File tree

6 files changed

+78
-11
lines changed

6 files changed

+78
-11
lines changed

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,20 @@ selector.setValue(6);
263263
wire.getValue(); // 3
264264
```
265265

266+
You can define a dependencies list for dependencies of options.
267+
268+
```ts
269+
const { n } = props;
270+
const wire = useWire(null, 4);
271+
const selector = useSelector(
272+
{
273+
get: ({ get }) => get(wire) * n,
274+
set: ({ set }, value) => set(wire, value / n),
275+
},
276+
[n],
277+
);
278+
```
279+
266280
### `createSelector` function
267281

268282
`createSelector` creates a new selector. It can be used outside of the react.

index.d.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
* @packageDocumentation
55
*/
66

7+
import { DependencyList } from 'react';
78
import { Dispatch } from 'react';
89
import { SetStateAction } from 'react';
910

@@ -178,10 +179,12 @@ export declare function useInterceptor<W extends StateWire<any>>(
178179

179180
export declare function useSelector<V, Fns = {}>(
180181
options: WritableSelectorOptions<V>,
182+
deps?: DependencyList,
181183
): Wire<V, Fns>;
182184

183185
export declare function useSelector<V, Fns = {}>(
184186
options: ReadOnlySelectorOptions<V>,
187+
deps?: DependencyList,
185188
): ReadonlyWire<V, Fns>;
186189

187190
export declare function useSubscribe<V>(

src/selector/use-selector.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useMemo } from 'react';
1+
import { DependencyList, useMemo } from 'react';
22
import { useFnsWire } from '../fn-wire/use-fns-wire';
33
import {
44
ReadOnlySelectorOptions,
@@ -10,14 +10,17 @@ import { ReadonlyWire, Wire } from '../wire/wire';
1010

1111
export function useSelector<V, Fns = {}>(
1212
options: WritableSelectorOptions<V>,
13+
deps?: DependencyList,
1314
): Wire<V, Fns>;
1415
export function useSelector<V, Fns = {}>(
1516
options: ReadOnlySelectorOptions<V>,
17+
deps?: DependencyList,
1618
): ReadonlyWire<V, Fns>;
1719
export function useSelector<V, Fns = {}>(
1820
options: SelectorOptions<V>,
21+
deps?: DependencyList,
1922
): ReadonlyWire<V, Fns> | Wire<V, Fns> {
2023
const fnsWire = useFnsWire<Fns>(null);
21-
const selector = useStateSelector<V>(options);
24+
const selector = useStateSelector<V>(options, deps);
2225
return useMemo(() => ({ ...fnsWire, ...selector }), [fnsWire, selector]);
2326
}

src/state-selector/create-state-selector.ts

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ import { createStateWireGuard } from '../state-wire/state-wire';
77
import { StateWire } from '../types';
88
import { Defined } from '../utils/type-utils';
99

10-
type DisconnectFunction = () => void;
11-
type ConnectFunction = () => DisconnectFunction;
10+
type ReconnectFunction<O> = (options?: O) => void;
11+
type ConnectFunction<O> = () => ReconnectFunction<O>;
1212

1313
export type GetWireValue = <V>(wire: ReadonlyStateWire<V>) => V;
1414
export type SetWireValue = <V>(wire: StateWire<V>, value: Defined<V>) => void;
@@ -29,20 +29,22 @@ export interface SelectorOptions<V> {
2929

3030
export function createStateSelector<V>(
3131
options: WritableSelectorOptions<V>,
32-
): [StateWire<V>, ConnectFunction];
32+
): [StateWire<V>, ConnectFunction<WritableSelectorOptions<V>>];
3333
export function createStateSelector<V>(
3434
options: ReadOnlySelectorOptions<V>,
35-
): [ReadonlyStateWire<V>, ConnectFunction];
35+
): [ReadonlyStateWire<V>, ConnectFunction<ReadOnlySelectorOptions<V>>];
3636
export function createStateSelector<V>(
3737
options: SelectorOptions<V>,
38-
): [ReadonlyStateWire<V> | StateWire<V>, ConnectFunction] {
38+
):
39+
| [ReadonlyStateWire<V>, ConnectFunction<ReadOnlySelectorOptions<V>>]
40+
| [StateWire<V>, ConnectFunction<WritableSelectorOptions<V>>] {
3941
const key: string = 'value';
4042
const activeWires: {
4143
current: Map<ReadonlyStateWire<any>, null | (() => void)>;
4244
} = {
4345
current: new Map(),
4446
};
45-
const { get: getOption, set: setOption } = options;
47+
let { get: getOption, set: setOption } = options;
4648
const get = <V>(wire: ReadonlyStateWire<V>) => {
4749
activeWires.current.set(wire, null);
4850
return wire.getValue();
@@ -92,7 +94,16 @@ export function createStateSelector<V>(
9294
};
9395

9496
const connect = () => {
95-
return update();
97+
const disconnect = update();
98+
return (options?: SelectorOptions<V>) => {
99+
if (options) {
100+
getOption = options.get;
101+
setOption = options.set;
102+
update();
103+
} else {
104+
disconnect();
105+
}
106+
};
96107
};
97108

98109
const getValue = () => {

src/state-selector/use-state-selector.test.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,23 @@ describe('use state selector', () => {
2727
});
2828
expect(result.current.value).toBe(8);
2929
});
30+
it('should have updated value when dependencies changed', () => {
31+
const { result, rerender } = renderHook(
32+
({ mul }: { mul: number }) => {
33+
const wire = useStateWire(null, 5);
34+
const selector = useStateSelector(
35+
{ get: ({ get }) => get(wire) * mul },
36+
[mul],
37+
);
38+
const value = useWireValue(selector);
39+
40+
return { value, wire };
41+
},
42+
{ initialProps: { mul: 2 } },
43+
);
44+
act(() => {
45+
rerender({ mul: 3 });
46+
});
47+
expect(result.current.value).toBe(15);
48+
});
3049
});

src/state-selector/use-state-selector.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useEffect, useState } from 'react';
1+
import { DependencyList, useEffect, useRef, useState } from 'react';
22
import { ReadonlyStateWire } from '../state-wire/readonly-state-wire';
33
import { StateWire } from '../state-wire/state-wire';
44
import {
@@ -10,18 +10,35 @@ import {
1010

1111
export function useStateSelector<V>(
1212
options: WritableSelectorOptions<V>,
13+
deps?: DependencyList,
1314
): StateWire<V>;
1415
export function useStateSelector<V>(
1516
options: ReadOnlySelectorOptions<V>,
17+
deps?: DependencyList,
1618
): ReadonlyStateWire<V>;
1719
export function useStateSelector<V>(
1820
options: SelectorOptions<V>,
21+
deps: DependencyList = [],
1922
): ReadonlyStateWire<V> | StateWire<V> {
2023
const [[selector, connect]] = useState(() => {
2124
return createStateSelector<V>(options);
2225
});
26+
27+
const reconnectRef = useRef<ReturnType<typeof connect> | null>(null);
28+
29+
useEffect(() => {
30+
const reconnect = reconnectRef.current;
31+
reconnect && reconnect(options);
32+
// eslint-disable-next-line
33+
}, deps);
34+
2335
useEffect(() => {
24-
return connect();
36+
const reconnect = connect();
37+
reconnectRef.current = reconnect;
38+
return () => {
39+
reconnect();
40+
reconnectRef.current = null;
41+
};
2542
}, [connect]);
2643

2744
return selector;

0 commit comments

Comments
 (0)