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.
Files changed (61) hide show
  1. package/README.md +29 -0
  2. package/lib/index.d.ts +22 -19
  3. package/lib/index.js +22 -19
  4. package/lib/index.js.map +1 -1
  5. package/lib/types.d.ts +3 -3
  6. package/lib/types.js +1 -1
  7. package/lib/use-callback-async.d.ts +2 -2
  8. package/lib/use-callback-async.js +19 -21
  9. package/lib/use-callback-async.js.map +1 -1
  10. package/lib/use-effect-async.d.ts +2 -2
  11. package/lib/use-effect-async.js +19 -19
  12. package/lib/use-finite-state-machine.d.ts +3 -3
  13. package/lib/use-finite-state-machine.js +13 -13
  14. package/lib/use-force-update.d.ts +1 -1
  15. package/lib/use-force-update.js +8 -8
  16. package/lib/use-get-set.d.ts +1 -0
  17. package/lib/use-get-set.js +16 -0
  18. package/lib/use-get-set.js.map +1 -0
  19. package/lib/use-iife.d.ts +2 -2
  20. package/lib/use-iife.js +20 -20
  21. package/lib/use-increment.d.ts +1 -1
  22. package/lib/use-increment.js +11 -11
  23. package/lib/use-intersection-observer.d.ts +3 -0
  24. package/lib/use-intersection-observer.js +19 -0
  25. package/lib/use-intersection-observer.js.map +1 -0
  26. package/lib/use-is-first-render.d.ts +1 -1
  27. package/lib/use-is-first-render.js +23 -23
  28. package/lib/use-is-mounted.d.ts +1 -1
  29. package/lib/use-is-mounted.js +15 -15
  30. package/lib/use-memo-with-cleanup.d.ts +2 -0
  31. package/lib/use-memo-with-cleanup.js +9 -0
  32. package/lib/use-memo-with-cleanup.js.map +1 -0
  33. package/lib/use-mount-async.d.ts +1 -1
  34. package/lib/use-mount-async.js +4 -4
  35. package/lib/use-mount.d.ts +2 -2
  36. package/lib/use-mount.js +4 -4
  37. package/lib/use-multiple-selection.d.ts +9 -9
  38. package/lib/use-multiple-selection.js +49 -49
  39. package/lib/use-render-counter.d.ts +1 -1
  40. package/lib/use-render-counter.js +6 -6
  41. package/lib/use-resize-observer.d.ts +2 -2
  42. package/lib/use-resize-observer.js +13 -13
  43. package/lib/use-single-selection.d.ts +7 -7
  44. package/lib/use-single-selection.js +22 -22
  45. package/lib/use-state-cycle.d.ts +2 -2
  46. package/lib/use-state-cycle.js +15 -15
  47. package/lib/use-step.d.ts +2 -2
  48. package/lib/use-step.js +25 -25
  49. package/lib/use-toggle.d.ts +1 -1
  50. package/lib/use-toggle.js +8 -8
  51. package/lib/use-unmount.d.ts +1 -1
  52. package/lib/use-unmount.js +4 -4
  53. package/lib/use-update-effect.d.ts +2 -2
  54. package/lib/use-update-effect.js +10 -10
  55. package/package.json +1 -1
  56. package/src/index.ts +3 -0
  57. package/src/use-callback-async.ts +10 -8
  58. package/src/use-get-set.ts +19 -0
  59. package/src/use-intersection-observer.ts +44 -0
  60. package/src/use-memo-with-cleanup.ts +15 -0
  61. 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
@@ -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];
@@ -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
@@ -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
@@ -1 +1 @@
1
- export declare function useUnmount(effect: () => void): void;
1
+ export declare function useUnmount(effect: () => void): void;
@@ -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;
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "extra-react-hooks",
3
- "version": "0.6.2",
3
+ "version": "0.6.4",
4
4
  "description": "",
5
5
  "keywords": [],
6
6
  "files": [
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 { useLayoutEffect, useCallback, useMemo, DependencyList } from 'react'
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 abort = Symbol()
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 = useMemo(() => new AbortController(), deps)
12
- useLayoutEffect(() => {
13
- return () => controller.abort(abort)
14
- }, [controller])
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 !== abort) throw err
23
+ if (err !== symbolAbort) throw err
22
24
  }
23
25
  })
24
- }, [controller])
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
+ }