extra-react-hooks 0.6.2 → 0.6.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -0
- package/lib/index.d.ts +22 -19
- package/lib/index.js +22 -19
- package/lib/index.js.map +1 -1
- package/lib/types.d.ts +3 -3
- package/lib/types.js +1 -1
- package/lib/use-callback-async.d.ts +2 -2
- package/lib/use-callback-async.js +19 -21
- package/lib/use-callback-async.js.map +1 -1
- package/lib/use-effect-async.d.ts +2 -2
- package/lib/use-effect-async.js +19 -19
- package/lib/use-finite-state-machine.d.ts +3 -3
- package/lib/use-finite-state-machine.js +13 -13
- package/lib/use-force-update.d.ts +1 -1
- package/lib/use-force-update.js +8 -8
- package/lib/use-get-set.d.ts +1 -0
- package/lib/use-get-set.js +16 -0
- package/lib/use-get-set.js.map +1 -0
- package/lib/use-iife.d.ts +2 -2
- package/lib/use-iife.js +20 -20
- package/lib/use-increment.d.ts +1 -1
- package/lib/use-increment.js +11 -11
- package/lib/use-intersection-observer.d.ts +3 -0
- package/lib/use-intersection-observer.js +19 -0
- package/lib/use-intersection-observer.js.map +1 -0
- package/lib/use-is-first-render.d.ts +1 -1
- package/lib/use-is-first-render.js +23 -23
- package/lib/use-is-mounted.d.ts +1 -1
- package/lib/use-is-mounted.js +15 -15
- package/lib/use-memo-with-cleanup.d.ts +2 -0
- package/lib/use-memo-with-cleanup.js +9 -0
- package/lib/use-memo-with-cleanup.js.map +1 -0
- package/lib/use-mount-async.d.ts +1 -1
- package/lib/use-mount-async.js +4 -4
- package/lib/use-mount.d.ts +2 -2
- package/lib/use-mount.js +4 -4
- package/lib/use-multiple-selection.d.ts +9 -9
- package/lib/use-multiple-selection.js +49 -49
- package/lib/use-render-counter.d.ts +1 -1
- package/lib/use-render-counter.js +6 -6
- package/lib/use-resize-observer.d.ts +2 -2
- package/lib/use-resize-observer.js +13 -13
- package/lib/use-single-selection.d.ts +7 -7
- package/lib/use-single-selection.js +22 -22
- package/lib/use-state-cycle.d.ts +2 -2
- package/lib/use-state-cycle.js +15 -15
- package/lib/use-step.d.ts +2 -2
- package/lib/use-step.js +25 -25
- package/lib/use-toggle.d.ts +1 -1
- package/lib/use-toggle.js +8 -8
- package/lib/use-unmount.d.ts +1 -1
- package/lib/use-unmount.js +4 -4
- package/lib/use-update-effect.d.ts +2 -2
- package/lib/use-update-effect.js +10 -10
- package/package.json +1 -1
- package/src/index.ts +3 -0
- package/src/use-callback-async.ts +10 -8
- package/src/use-get-set.ts +19 -0
- package/src/use-intersection-observer.ts +44 -0
- package/src/use-memo-with-cleanup.ts +15 -0
- package/CHANGELOG.md +0 -227
|
@@ -1,50 +1,50 @@
|
|
|
1
|
-
import { useState, useMemo, useCallback } from 'react';
|
|
2
|
-
import { assert, toArray } from '@blackglory/prelude';
|
|
3
|
-
import { useIIFE } from "./use-iife.js";
|
|
4
|
-
export function useMultipleSelection(options, defaultSelectedIndexes = []) {
|
|
5
|
-
useIIFE(() => {
|
|
6
|
-
for (const index of defaultSelectedIndexes) {
|
|
7
|
-
assert(index >= 0 && index < options.length, 'The index of parameter defaultSelectedIndexes must be in the range of 0 to values.length');
|
|
8
|
-
}
|
|
9
|
-
}, [defaultSelectedIndexes]);
|
|
10
|
-
const [selectedIndexes, setSelectedIndexes] = useState(new Set(defaultSelectedIndexes));
|
|
11
|
-
return {
|
|
12
|
-
selectedIndexes: useMemo(() => toArray(selectedIndexes), [selectedIndexes]),
|
|
13
|
-
optionStates: useMemo(createOptionStates, [options, selectedIndexes]),
|
|
14
|
-
toggle: useCallback(toggle, [selectedIndexes]),
|
|
15
|
-
select: useCallback(select, []),
|
|
16
|
-
unselect: useCallback(unselect, [])
|
|
17
|
-
};
|
|
18
|
-
function createOptionStates() {
|
|
19
|
-
return options.map((_, index) => {
|
|
20
|
-
const selected = isSelected(index);
|
|
21
|
-
return { selected };
|
|
22
|
-
});
|
|
23
|
-
}
|
|
24
|
-
function isSelected(index) {
|
|
25
|
-
return selectedIndexes.has(index);
|
|
26
|
-
}
|
|
27
|
-
function toggle(index) {
|
|
28
|
-
if (isSelected(index)) {
|
|
29
|
-
unselect(index);
|
|
30
|
-
}
|
|
31
|
-
else {
|
|
32
|
-
select(index);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
function select(index) {
|
|
36
|
-
setSelectedIndexes(selectedIndexes => {
|
|
37
|
-
const result = new Set(selectedIndexes);
|
|
38
|
-
result.add(index);
|
|
39
|
-
return result;
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
function unselect(index) {
|
|
43
|
-
setSelectedIndexes(selectedIndexes => {
|
|
44
|
-
const result = new Set(selectedIndexes);
|
|
45
|
-
result.delete(index);
|
|
46
|
-
return result;
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
}
|
|
1
|
+
import { useState, useMemo, useCallback } from 'react';
|
|
2
|
+
import { assert, toArray } from '@blackglory/prelude';
|
|
3
|
+
import { useIIFE } from "./use-iife.js";
|
|
4
|
+
export function useMultipleSelection(options, defaultSelectedIndexes = []) {
|
|
5
|
+
useIIFE(() => {
|
|
6
|
+
for (const index of defaultSelectedIndexes) {
|
|
7
|
+
assert(index >= 0 && index < options.length, 'The index of parameter defaultSelectedIndexes must be in the range of 0 to values.length');
|
|
8
|
+
}
|
|
9
|
+
}, [defaultSelectedIndexes]);
|
|
10
|
+
const [selectedIndexes, setSelectedIndexes] = useState(new Set(defaultSelectedIndexes));
|
|
11
|
+
return {
|
|
12
|
+
selectedIndexes: useMemo(() => toArray(selectedIndexes), [selectedIndexes]),
|
|
13
|
+
optionStates: useMemo(createOptionStates, [options, selectedIndexes]),
|
|
14
|
+
toggle: useCallback(toggle, [selectedIndexes]),
|
|
15
|
+
select: useCallback(select, []),
|
|
16
|
+
unselect: useCallback(unselect, [])
|
|
17
|
+
};
|
|
18
|
+
function createOptionStates() {
|
|
19
|
+
return options.map((_, index) => {
|
|
20
|
+
const selected = isSelected(index);
|
|
21
|
+
return { selected };
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
function isSelected(index) {
|
|
25
|
+
return selectedIndexes.has(index);
|
|
26
|
+
}
|
|
27
|
+
function toggle(index) {
|
|
28
|
+
if (isSelected(index)) {
|
|
29
|
+
unselect(index);
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
select(index);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function select(index) {
|
|
36
|
+
setSelectedIndexes(selectedIndexes => {
|
|
37
|
+
const result = new Set(selectedIndexes);
|
|
38
|
+
result.add(index);
|
|
39
|
+
return result;
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
function unselect(index) {
|
|
43
|
+
setSelectedIndexes(selectedIndexes => {
|
|
44
|
+
const result = new Set(selectedIndexes);
|
|
45
|
+
result.delete(index);
|
|
46
|
+
return result;
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
50
|
//# sourceMappingURL=use-multiple-selection.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare function useRenderCounter(): number;
|
|
1
|
+
export declare function useRenderCounter(): number;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { useRef } from 'react';
|
|
2
|
-
export function useRenderCounter() {
|
|
3
|
-
const counter = useRef(0);
|
|
4
|
-
counter.current++;
|
|
5
|
-
return counter.current;
|
|
6
|
-
}
|
|
1
|
+
import { useRef } from 'react';
|
|
2
|
+
export function useRenderCounter() {
|
|
3
|
+
const counter = useRef(0);
|
|
4
|
+
counter.current++;
|
|
5
|
+
return counter.current;
|
|
6
|
+
}
|
|
7
7
|
//# sourceMappingURL=use-render-counter.js.map
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { RefObject, MutableRefObject } from 'react';
|
|
2
|
-
export declare function useResizeObserver(callback: ResizeObserverCallback, refs: Array<RefObject<HTMLElement> | MutableRefObject<HTMLElement>>): void;
|
|
1
|
+
import { RefObject, MutableRefObject } from 'react';
|
|
2
|
+
export declare function useResizeObserver(callback: ResizeObserverCallback, refs: Array<RefObject<HTMLElement> | MutableRefObject<HTMLElement>>): void;
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { useEffect } from 'react';
|
|
2
|
-
import { isntNull } from '@blackglory/prelude';
|
|
3
|
-
export function useResizeObserver(callback, refs) {
|
|
4
|
-
const deps = refs.map(x => x.current);
|
|
5
|
-
useEffect(() => {
|
|
6
|
-
const observer = new ResizeObserver(callback);
|
|
7
|
-
refs
|
|
8
|
-
.map(x => x.current)
|
|
9
|
-
.filter(isntNull)
|
|
10
|
-
.forEach(x => observer.observe(x));
|
|
11
|
-
return () => observer.disconnect();
|
|
12
|
-
}, deps);
|
|
13
|
-
}
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
import { isntNull } from '@blackglory/prelude';
|
|
3
|
+
export function useResizeObserver(callback, refs) {
|
|
4
|
+
const deps = refs.map(x => x.current);
|
|
5
|
+
useEffect(() => {
|
|
6
|
+
const observer = new ResizeObserver(callback);
|
|
7
|
+
refs
|
|
8
|
+
.map(x => x.current)
|
|
9
|
+
.filter(isntNull)
|
|
10
|
+
.forEach(x => observer.observe(x));
|
|
11
|
+
return () => observer.disconnect();
|
|
12
|
+
}, deps);
|
|
13
|
+
}
|
|
14
14
|
//# sourceMappingURL=use-resize-observer.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { NonEmptyArray } from '@blackglory/prelude';
|
|
2
|
-
import { IOptionState } from "./types.js";
|
|
3
|
-
export declare function useSingleSelection<T>(options: NonEmptyArray<T>, defaultSelectedIndex: number): {
|
|
4
|
-
selectedIndex: number;
|
|
5
|
-
optionStates: IOptionState[];
|
|
6
|
-
select: (index: number) => void;
|
|
7
|
-
};
|
|
1
|
+
import { NonEmptyArray } from '@blackglory/prelude';
|
|
2
|
+
import { IOptionState } from "./types.js";
|
|
3
|
+
export declare function useSingleSelection<T>(options: NonEmptyArray<T>, defaultSelectedIndex: number): {
|
|
4
|
+
selectedIndex: number;
|
|
5
|
+
optionStates: IOptionState[];
|
|
6
|
+
select: (index: number) => void;
|
|
7
|
+
};
|
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
import { useState, useMemo, useCallback } from 'react';
|
|
2
|
-
import { assert } from '@blackglory/prelude';
|
|
3
|
-
import { useIIFE } from "./use-iife.js";
|
|
4
|
-
export function useSingleSelection(options, defaultSelectedIndex) {
|
|
5
|
-
useIIFE(() => {
|
|
6
|
-
assert(defaultSelectedIndex >= 0 && defaultSelectedIndex < options.length, 'The parameter defaultSelectedIndex must be in the range of 0 to values.length');
|
|
7
|
-
}, [defaultSelectedIndex]);
|
|
8
|
-
const [selectedIndex, setSelectedIndex] = useState(defaultSelectedIndex);
|
|
9
|
-
return {
|
|
10
|
-
selectedIndex,
|
|
11
|
-
optionStates: useMemo(createOptionStates, [options, selectedIndex]),
|
|
12
|
-
select: useCallback(select, [])
|
|
13
|
-
};
|
|
14
|
-
function createOptionStates() {
|
|
15
|
-
return options.map((_, index) => ({
|
|
16
|
-
selected: index === selectedIndex
|
|
17
|
-
}));
|
|
18
|
-
}
|
|
19
|
-
function select(index) {
|
|
20
|
-
setSelectedIndex(index);
|
|
21
|
-
}
|
|
22
|
-
}
|
|
1
|
+
import { useState, useMemo, useCallback } from 'react';
|
|
2
|
+
import { assert } from '@blackglory/prelude';
|
|
3
|
+
import { useIIFE } from "./use-iife.js";
|
|
4
|
+
export function useSingleSelection(options, defaultSelectedIndex) {
|
|
5
|
+
useIIFE(() => {
|
|
6
|
+
assert(defaultSelectedIndex >= 0 && defaultSelectedIndex < options.length, 'The parameter defaultSelectedIndex must be in the range of 0 to values.length');
|
|
7
|
+
}, [defaultSelectedIndex]);
|
|
8
|
+
const [selectedIndex, setSelectedIndex] = useState(defaultSelectedIndex);
|
|
9
|
+
return {
|
|
10
|
+
selectedIndex,
|
|
11
|
+
optionStates: useMemo(createOptionStates, [options, selectedIndex]),
|
|
12
|
+
select: useCallback(select, [])
|
|
13
|
+
};
|
|
14
|
+
function createOptionStates() {
|
|
15
|
+
return options.map((_, index) => ({
|
|
16
|
+
selected: index === selectedIndex
|
|
17
|
+
}));
|
|
18
|
+
}
|
|
19
|
+
function select(index) {
|
|
20
|
+
setSelectedIndex(index);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
23
|
//# sourceMappingURL=use-single-selection.js.map
|
package/lib/use-state-cycle.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { NonEmptyArray } from '@blackglory/prelude';
|
|
2
|
-
export declare function useStateCycle<T>(orderedStates: NonEmptyArray<T>, initialStateIndex?: number): [state: T, next: () => void];
|
|
1
|
+
import { NonEmptyArray } from '@blackglory/prelude';
|
|
2
|
+
export declare function useStateCycle<T>(orderedStates: NonEmptyArray<T>, initialStateIndex?: number): [state: T, next: () => void];
|
package/lib/use-state-cycle.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import { useState, useCallback } from 'react';
|
|
2
|
-
export function useStateCycle(orderedStates, initialStateIndex = 0) {
|
|
3
|
-
const [index, setIndex] = useState(initialStateIndex);
|
|
4
|
-
return [orderedStates[index], useCallback(next, [orderedStates])];
|
|
5
|
-
function next() {
|
|
6
|
-
setIndex(index => {
|
|
7
|
-
if (index + 1 < orderedStates.length) {
|
|
8
|
-
return index + 1;
|
|
9
|
-
}
|
|
10
|
-
else {
|
|
11
|
-
return 0;
|
|
12
|
-
}
|
|
13
|
-
});
|
|
14
|
-
}
|
|
15
|
-
}
|
|
1
|
+
import { useState, useCallback } from 'react';
|
|
2
|
+
export function useStateCycle(orderedStates, initialStateIndex = 0) {
|
|
3
|
+
const [index, setIndex] = useState(initialStateIndex);
|
|
4
|
+
return [orderedStates[index], useCallback(next, [orderedStates])];
|
|
5
|
+
function next() {
|
|
6
|
+
setIndex(index => {
|
|
7
|
+
if (index + 1 < orderedStates.length) {
|
|
8
|
+
return index + 1;
|
|
9
|
+
}
|
|
10
|
+
else {
|
|
11
|
+
return 0;
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
16
|
//# sourceMappingURL=use-state-cycle.js.map
|
package/lib/use-step.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { NonEmptyArray } from '@blackglory/prelude';
|
|
2
|
-
export declare function useStep<T>(steps: NonEmptyArray<T>, initialStepIndex?: number): [currentStep: T, next: () => void, previous: () => void];
|
|
1
|
+
import { NonEmptyArray } from '@blackglory/prelude';
|
|
2
|
+
export declare function useStep<T>(steps: NonEmptyArray<T>, initialStepIndex?: number): [currentStep: T, next: () => void, previous: () => void];
|
package/lib/use-step.js
CHANGED
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
import { useState, useCallback } from 'react';
|
|
2
|
-
export function useStep(steps, initialStepIndex = 0) {
|
|
3
|
-
const [index, setIndex] = useState(initialStepIndex);
|
|
4
|
-
return [steps[index], useCallback(next, [steps]), useCallback(previous, [])];
|
|
5
|
-
function next() {
|
|
6
|
-
setIndex(index => {
|
|
7
|
-
if (index === steps.length - 1) {
|
|
8
|
-
return index;
|
|
9
|
-
}
|
|
10
|
-
else {
|
|
11
|
-
return index + 1;
|
|
12
|
-
}
|
|
13
|
-
});
|
|
14
|
-
}
|
|
15
|
-
function previous() {
|
|
16
|
-
setIndex(index => {
|
|
17
|
-
if (index === 0) {
|
|
18
|
-
return index;
|
|
19
|
-
}
|
|
20
|
-
else {
|
|
21
|
-
return index - 1;
|
|
22
|
-
}
|
|
23
|
-
});
|
|
24
|
-
}
|
|
25
|
-
}
|
|
1
|
+
import { useState, useCallback } from 'react';
|
|
2
|
+
export function useStep(steps, initialStepIndex = 0) {
|
|
3
|
+
const [index, setIndex] = useState(initialStepIndex);
|
|
4
|
+
return [steps[index], useCallback(next, [steps]), useCallback(previous, [])];
|
|
5
|
+
function next() {
|
|
6
|
+
setIndex(index => {
|
|
7
|
+
if (index === steps.length - 1) {
|
|
8
|
+
return index;
|
|
9
|
+
}
|
|
10
|
+
else {
|
|
11
|
+
return index + 1;
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
function previous() {
|
|
16
|
+
setIndex(index => {
|
|
17
|
+
if (index === 0) {
|
|
18
|
+
return index;
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
return index - 1;
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
26
|
//# sourceMappingURL=use-step.js.map
|
package/lib/use-toggle.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare function useToggle(initialState?: boolean): [on: boolean, toggle: () => void];
|
|
1
|
+
export declare function useToggle(initialState?: boolean): [on: boolean, toggle: () => void];
|
package/lib/use-toggle.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { useState, useCallback } from 'react';
|
|
2
|
-
export function useToggle(initialState = false) {
|
|
3
|
-
const [state, setState] = useState(initialState);
|
|
4
|
-
return [state, useCallback(toggle, [])];
|
|
5
|
-
function toggle() {
|
|
6
|
-
setState(state => !state);
|
|
7
|
-
}
|
|
8
|
-
}
|
|
1
|
+
import { useState, useCallback } from 'react';
|
|
2
|
+
export function useToggle(initialState = false) {
|
|
3
|
+
const [state, setState] = useState(initialState);
|
|
4
|
+
return [state, useCallback(toggle, [])];
|
|
5
|
+
function toggle() {
|
|
6
|
+
setState(state => !state);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
9
|
//# sourceMappingURL=use-toggle.js.map
|
package/lib/use-unmount.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare function useUnmount(effect: () => void): void;
|
|
1
|
+
export declare function useUnmount(effect: () => void): void;
|
package/lib/use-unmount.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { useEffect } from 'react';
|
|
2
|
-
export function useUnmount(effect) {
|
|
3
|
-
useEffect(() => effect, []);
|
|
4
|
-
}
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
export function useUnmount(effect) {
|
|
3
|
+
useEffect(() => effect, []);
|
|
4
|
+
}
|
|
5
5
|
//# sourceMappingURL=use-unmount.js.map
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { EffectCallback } from 'react';
|
|
2
|
-
export declare function useUpdateEffect(effect: EffectCallback, deps?: React.DependencyList): void;
|
|
1
|
+
import { EffectCallback } from 'react';
|
|
2
|
+
export declare function useUpdateEffect(effect: EffectCallback, deps?: React.DependencyList): void;
|
package/lib/use-update-effect.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { useEffect } from 'react';
|
|
2
|
-
import { useIsFirstRender } from './use-is-first-render.js';
|
|
3
|
-
export function useUpdateEffect(effect, deps) {
|
|
4
|
-
const isFirstRender = useIsFirstRender();
|
|
5
|
-
useEffect(() => {
|
|
6
|
-
if (!isFirstRender()) {
|
|
7
|
-
return effect();
|
|
8
|
-
}
|
|
9
|
-
}, deps);
|
|
10
|
-
}
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
import { useIsFirstRender } from './use-is-first-render.js';
|
|
3
|
+
export function useUpdateEffect(effect, deps) {
|
|
4
|
+
const isFirstRender = useIsFirstRender();
|
|
5
|
+
useEffect(() => {
|
|
6
|
+
if (!isFirstRender()) {
|
|
7
|
+
return effect();
|
|
8
|
+
}
|
|
9
|
+
}, deps);
|
|
10
|
+
}
|
|
11
11
|
//# sourceMappingURL=use-update-effect.js.map
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -13,7 +13,10 @@ export * from './use-multiple-selection.js'
|
|
|
13
13
|
export * from './use-state-cycle.js'
|
|
14
14
|
export * from './use-toggle.js'
|
|
15
15
|
export * from './use-resize-observer.js'
|
|
16
|
+
export * from './use-intersection-observer.js'
|
|
16
17
|
export * from './use-step.js'
|
|
17
18
|
export * from './use-force-update.js'
|
|
18
19
|
export * from './use-finite-state-machine.js'
|
|
19
20
|
export * from './use-iife.js'
|
|
21
|
+
export * from './use-memo-with-cleanup.js'
|
|
22
|
+
export * from './use-get-set.js'
|
|
@@ -1,25 +1,27 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useCallback, DependencyList } from 'react'
|
|
2
2
|
import { go } from '@blackglory/prelude'
|
|
3
3
|
import { AbortController } from 'extra-abort'
|
|
4
|
+
import { useMemoWithCleanup } from './use-memo-with-cleanup.js'
|
|
4
5
|
|
|
5
|
-
const
|
|
6
|
+
const symbolAbort = Symbol()
|
|
6
7
|
|
|
7
8
|
export function useCallbackAsync<Args extends unknown[]>(
|
|
8
9
|
callback: (...args: [...args: Args, signal: AbortSignal]) => Promise<void>
|
|
9
10
|
, deps: DependencyList
|
|
10
11
|
): (...args: Args) => void {
|
|
11
|
-
const controller =
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
const controller = useMemoWithCleanup(
|
|
13
|
+
() => new AbortController()
|
|
14
|
+
, controller => controller.abort(symbolAbort)
|
|
15
|
+
, deps
|
|
16
|
+
)
|
|
15
17
|
|
|
16
18
|
return useCallback((...args: Args) => {
|
|
17
19
|
go(async () => {
|
|
18
20
|
try {
|
|
19
21
|
await callback(...args, controller.signal)
|
|
20
22
|
} catch (err) {
|
|
21
|
-
if (err !==
|
|
23
|
+
if (err !== symbolAbort) throw err
|
|
22
24
|
}
|
|
23
25
|
})
|
|
24
|
-
},
|
|
26
|
+
}, deps)
|
|
25
27
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { useRef, useCallback } from 'react'
|
|
2
|
+
import { useForceUpdate } from './use-force-update.js'
|
|
3
|
+
|
|
4
|
+
export function useGetSet<T>(
|
|
5
|
+
initialValue: T
|
|
6
|
+
): [get: () => T, set: (value: T) => void] {
|
|
7
|
+
const forceUpdate = useForceUpdate()
|
|
8
|
+
const ref = useRef<T>(initialValue)
|
|
9
|
+
|
|
10
|
+
return [
|
|
11
|
+
useCallback(() => ref.current, [])
|
|
12
|
+
, useCallback((value: T) => {
|
|
13
|
+
if (ref.current !== value) {
|
|
14
|
+
ref.current = value
|
|
15
|
+
forceUpdate()
|
|
16
|
+
}
|
|
17
|
+
}, [])
|
|
18
|
+
]
|
|
19
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { useEffect, RefObject, MutableRefObject } from 'react'
|
|
2
|
+
import { isntNull } from '@blackglory/prelude'
|
|
3
|
+
|
|
4
|
+
export function useIntersectionObserver(
|
|
5
|
+
callback: IntersectionObserverCallback
|
|
6
|
+
, options: IntersectionObserverInit | undefined
|
|
7
|
+
, refs: Array<RefObject<HTMLElement> | MutableRefObject<HTMLElement>>
|
|
8
|
+
): void
|
|
9
|
+
export function useIntersectionObserver(
|
|
10
|
+
callback: IntersectionObserverCallback
|
|
11
|
+
, refs: Array<RefObject<HTMLElement> | MutableRefObject<HTMLElement>>
|
|
12
|
+
): void
|
|
13
|
+
export function useIntersectionObserver(...args:
|
|
14
|
+
| [
|
|
15
|
+
callback: IntersectionObserverCallback
|
|
16
|
+
, options: IntersectionObserverInit | undefined
|
|
17
|
+
, refs: Array<RefObject<HTMLElement> | MutableRefObject<HTMLElement>>
|
|
18
|
+
]
|
|
19
|
+
| [
|
|
20
|
+
callback: IntersectionObserverCallback
|
|
21
|
+
, refs: Array<RefObject<HTMLElement> | MutableRefObject<HTMLElement>>
|
|
22
|
+
]
|
|
23
|
+
): void {
|
|
24
|
+
if (args.length === 2) {
|
|
25
|
+
const [callback, refs] = args
|
|
26
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
27
|
+
return useIntersectionObserver(callback, undefined, refs)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const [callback, options, refs] = args
|
|
31
|
+
const deps = refs.map(x => x.current)
|
|
32
|
+
|
|
33
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
const observer = new IntersectionObserver(callback, options)
|
|
36
|
+
|
|
37
|
+
refs
|
|
38
|
+
.map(x => x.current)
|
|
39
|
+
.filter(isntNull)
|
|
40
|
+
.forEach(x => observer.observe(x))
|
|
41
|
+
|
|
42
|
+
return () => observer.disconnect()
|
|
43
|
+
}, deps)
|
|
44
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { useMemo, useEffect } from 'react'
|
|
2
|
+
|
|
3
|
+
export function useMemoWithCleanup<T>(
|
|
4
|
+
factory: () => T
|
|
5
|
+
, cleanup: (value: T) => void
|
|
6
|
+
, deps?: React.DependencyList
|
|
7
|
+
): T {
|
|
8
|
+
const value = useMemo(factory, deps)
|
|
9
|
+
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
return () => cleanup(value)
|
|
12
|
+
}, deps)
|
|
13
|
+
|
|
14
|
+
return value
|
|
15
|
+
}
|