mirror of
https://github.com/streamich/react-use.git
synced 2026-01-25 14:17:16 +00:00
Merge pull request #1740 from streamich/refactoring
chore: refactoring and rearrangement.
This commit is contained in:
commit
24ecb0d236
@ -23,7 +23,7 @@ const Demo = () => {
|
||||
Or as render-prop:
|
||||
|
||||
```jsx
|
||||
import UseKey from 'react-use/lib/comps/UseKey';
|
||||
import UseKey from 'react-use/lib/component/UseKey';
|
||||
|
||||
<UseKey filter='a' fn={() => alert('"a" key pressed!')} />
|
||||
```
|
||||
|
||||
32
package.json
32
package.json
@ -47,20 +47,19 @@
|
||||
},
|
||||
"homepage": "https://github.com/streamich/react-use#readme",
|
||||
"dependencies": {
|
||||
"@types/js-cookie": "2.2.6",
|
||||
"@xobotyi/scrollbar-width": "1.9.5",
|
||||
"copy-to-clipboard": "^3.2.0",
|
||||
"@xobotyi/scrollbar-width": "^1.9.5",
|
||||
"copy-to-clipboard": "^3.3.1",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"fast-shallow-equal": "^1.0.0",
|
||||
"js-cookie": "^2.2.1",
|
||||
"nano-css": "^5.2.1",
|
||||
"nano-css": "^5.3.1",
|
||||
"react-universal-interface": "^0.6.2",
|
||||
"resize-observer-polyfill": "^1.5.1",
|
||||
"screenfull": "^5.0.0",
|
||||
"screenfull": "^5.1.0",
|
||||
"set-harmonic-interval": "^1.0.1",
|
||||
"throttle-debounce": "^2.1.0",
|
||||
"throttle-debounce": "^3.0.1",
|
||||
"ts-easing": "^0.2.0",
|
||||
"tslib": "^2.0.0"
|
||||
"tslib": "^2.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0",
|
||||
@ -82,9 +81,10 @@
|
||||
"@storybook/addon-options": "5.3.21",
|
||||
"@storybook/react": "6.1.15",
|
||||
"@testing-library/react": "11.2.3",
|
||||
"@testing-library/react-hooks": "3.7.0",
|
||||
"@testing-library/react-hooks": "5.0.3",
|
||||
"@types/jest": "26.0.20",
|
||||
"@types/react": "16.9.11",
|
||||
"@types/js-cookie": "2.2.6",
|
||||
"@types/react": "17.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "4.14.1",
|
||||
"@typescript-eslint/parser": "4.14.1",
|
||||
"babel-core": "6.26.3",
|
||||
@ -92,13 +92,13 @@
|
||||
"babel-loader": "8.2.2",
|
||||
"babel-plugin-dynamic-import-node": "2.3.3",
|
||||
"eslint": "7.18.0",
|
||||
"eslint-config-react-app": "5.2.1",
|
||||
"eslint-config-react-app": "6.0.0",
|
||||
"eslint-plugin-flowtype": "5.2.0",
|
||||
"eslint-plugin-import": "2.22.1",
|
||||
"eslint-plugin-jsx-a11y": "6.4.1",
|
||||
"eslint-plugin-react": "7.22.0",
|
||||
"eslint-plugin-react-hooks": "4.2.0",
|
||||
"fork-ts-checker-webpack-plugin": "5.2.1",
|
||||
"fork-ts-checker-webpack-plugin": "6.1.0",
|
||||
"gh-pages": "3.1.0",
|
||||
"husky": "4.3.8",
|
||||
"jest": "26.6.3",
|
||||
@ -108,11 +108,11 @@
|
||||
"markdown-loader": "6.0.0",
|
||||
"prettier": "2.2.1",
|
||||
"raf-stub": "3.0.0",
|
||||
"react": "16.14.0",
|
||||
"react-dom": "16.14.0",
|
||||
"react": "17.0.1",
|
||||
"react-dom": "17.0.1",
|
||||
"react-frame-component": "4.1.3",
|
||||
"react-spring": "8.0.27",
|
||||
"react-test-renderer": "16.14.0",
|
||||
"react-test-renderer": "17.0.1",
|
||||
"rebound": "0.1.0",
|
||||
"redux-logger": "3.0.6",
|
||||
"redux-thunk": "2.3.0",
|
||||
@ -122,7 +122,7 @@
|
||||
"ts-jest": "26.5.0",
|
||||
"ts-loader": "8.0.14",
|
||||
"ts-node": "9.1.1",
|
||||
"typescript": "3.9.7"
|
||||
"typescript": "4.1.3"
|
||||
},
|
||||
"config": {
|
||||
"commitizen": {
|
||||
@ -156,7 +156,7 @@
|
||||
]
|
||||
},
|
||||
"volta": {
|
||||
"node": "10.23.2",
|
||||
"node": "10.23.1",
|
||||
"yarn": "1.22.10"
|
||||
},
|
||||
"collective": {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import useKey from '../useKey';
|
||||
import createRenderProp from '../util/createRenderProp';
|
||||
import createRenderProp from '../factory/createRenderProp';
|
||||
|
||||
const UseKey = createRenderProp(useKey, ({ filter, fn, deps, ...rest }) => [filter, fn, rest, deps]);
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { useEffect, useState, useMemo } from 'react';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { off, on } from '../misc/util';
|
||||
|
||||
const createBreakpoint = (
|
||||
breakpoints: { [name: string]: number } = { laptopL: 1440, laptop: 1024, tablet: 768 }
|
||||
@ -10,9 +11,9 @@ const createBreakpoint = (
|
||||
setScreen(window.innerWidth);
|
||||
};
|
||||
setSideScreen();
|
||||
window.addEventListener('resize', setSideScreen);
|
||||
on(window, 'resize', setSideScreen);
|
||||
return () => {
|
||||
window.removeEventListener('resize', setSideScreen);
|
||||
off(window, 'resize', setSideScreen);
|
||||
};
|
||||
});
|
||||
const sortedBreakpoints = useMemo(() => Object.entries(breakpoints).sort((a, b) => (a[1] >= b[1] ? 1 : -1)), [
|
||||
@ -1,6 +1,6 @@
|
||||
import { useState } from 'react';
|
||||
import useEffectOnce from './useEffectOnce';
|
||||
import useIsomorphicLayoutEffect from './useIsomorphicLayoutEffect';
|
||||
import useEffectOnce from '../useEffectOnce';
|
||||
import useIsomorphicLayoutEffect from '../useIsomorphicLayoutEffect';
|
||||
|
||||
export function createGlobalState<S = any>(initialState?: S) {
|
||||
const store: { state: S | undefined; setState: (state: S) => void; setters: any[] } = {
|
||||
235
src/factory/createHTMLMediaHook.ts
Normal file
235
src/factory/createHTMLMediaHook.ts
Normal file
@ -0,0 +1,235 @@
|
||||
import * as React from 'react';
|
||||
import { useEffect, useRef } from 'react';
|
||||
import useSetState from '../useSetState';
|
||||
import parseTimeRanges from '../misc/parseTimeRanges';
|
||||
|
||||
export interface HTMLMediaProps extends React.AudioHTMLAttributes<any>, React.VideoHTMLAttributes<any> {
|
||||
src: string;
|
||||
}
|
||||
|
||||
export interface HTMLMediaState {
|
||||
buffered: any[];
|
||||
duration: number;
|
||||
paused: boolean;
|
||||
muted: boolean;
|
||||
time: number;
|
||||
volume: number;
|
||||
}
|
||||
|
||||
export interface HTMLMediaControls {
|
||||
play: () => Promise<void> | void;
|
||||
pause: () => void;
|
||||
mute: () => void;
|
||||
unmute: () => void;
|
||||
volume: (volume: number) => void;
|
||||
seek: (time: number) => void;
|
||||
}
|
||||
|
||||
type createHTMLMediaHookReturn = [
|
||||
React.ReactElement<HTMLMediaProps>,
|
||||
HTMLMediaState,
|
||||
HTMLMediaControls,
|
||||
{ current: HTMLAudioElement | null }
|
||||
];
|
||||
|
||||
export default function createHTMLMediaHook(tag: 'audio' | 'video') {
|
||||
return (elOrProps: HTMLMediaProps | React.ReactElement<HTMLMediaProps>): createHTMLMediaHookReturn => {
|
||||
let element: React.ReactElement<any> | undefined;
|
||||
let props: HTMLMediaProps;
|
||||
|
||||
if (React.isValidElement(elOrProps)) {
|
||||
element = elOrProps;
|
||||
props = element.props;
|
||||
} else {
|
||||
props = elOrProps as HTMLMediaProps;
|
||||
}
|
||||
|
||||
const [state, setState] = useSetState<HTMLMediaState>({
|
||||
buffered: [],
|
||||
time: 0,
|
||||
duration: 0,
|
||||
paused: true,
|
||||
muted: false,
|
||||
volume: 1,
|
||||
});
|
||||
const ref = useRef<HTMLAudioElement | null>(null);
|
||||
|
||||
const wrapEvent = (userEvent, proxyEvent?) => {
|
||||
return (event) => {
|
||||
try {
|
||||
proxyEvent && proxyEvent(event);
|
||||
} finally {
|
||||
userEvent && userEvent(event);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const onPlay = () => setState({ paused: false });
|
||||
const onPause = () => setState({ paused: true });
|
||||
const onVolumeChange = () => {
|
||||
const el = ref.current;
|
||||
if (!el) {
|
||||
return;
|
||||
}
|
||||
setState({
|
||||
muted: el.muted,
|
||||
volume: el.volume,
|
||||
});
|
||||
};
|
||||
const onDurationChange = () => {
|
||||
const el = ref.current;
|
||||
if (!el) {
|
||||
return;
|
||||
}
|
||||
const { duration, buffered } = el;
|
||||
setState({
|
||||
duration,
|
||||
buffered: parseTimeRanges(buffered),
|
||||
});
|
||||
};
|
||||
const onTimeUpdate = () => {
|
||||
const el = ref.current;
|
||||
if (!el) {
|
||||
return;
|
||||
}
|
||||
setState({ time: el.currentTime });
|
||||
};
|
||||
const onProgress = () => {
|
||||
const el = ref.current;
|
||||
if (!el) {
|
||||
return;
|
||||
}
|
||||
setState({ buffered: parseTimeRanges(el.buffered) });
|
||||
};
|
||||
|
||||
if (element) {
|
||||
element = React.cloneElement(element, {
|
||||
controls: false,
|
||||
...props,
|
||||
ref,
|
||||
onPlay: wrapEvent(props.onPlay, onPlay),
|
||||
onPause: wrapEvent(props.onPause, onPause),
|
||||
onVolumeChange: wrapEvent(props.onVolumeChange, onVolumeChange),
|
||||
onDurationChange: wrapEvent(props.onDurationChange, onDurationChange),
|
||||
onTimeUpdate: wrapEvent(props.onTimeUpdate, onTimeUpdate),
|
||||
onProgress: wrapEvent(props.onProgress, onProgress),
|
||||
});
|
||||
} else {
|
||||
element = React.createElement(tag, {
|
||||
controls: false,
|
||||
...props,
|
||||
ref,
|
||||
onPlay: wrapEvent(props.onPlay, onPlay),
|
||||
onPause: wrapEvent(props.onPause, onPause),
|
||||
onVolumeChange: wrapEvent(props.onVolumeChange, onVolumeChange),
|
||||
onDurationChange: wrapEvent(props.onDurationChange, onDurationChange),
|
||||
onTimeUpdate: wrapEvent(props.onTimeUpdate, onTimeUpdate),
|
||||
onProgress: wrapEvent(props.onProgress, onProgress),
|
||||
} as any); // TODO: fix this typing.
|
||||
}
|
||||
|
||||
// Some browsers return `Promise` on `.play()` and may throw errors
|
||||
// if one tries to execute another `.play()` or `.pause()` while that
|
||||
// promise is resolving. So we prevent that with this lock.
|
||||
// See: https://bugs.chromium.org/p/chromium/issues/detail?id=593273
|
||||
let lockPlay: boolean = false;
|
||||
|
||||
const controls = {
|
||||
play: () => {
|
||||
const el = ref.current;
|
||||
if (!el) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (!lockPlay) {
|
||||
const promise = el.play();
|
||||
const isPromise = typeof promise === 'object';
|
||||
|
||||
if (isPromise) {
|
||||
lockPlay = true;
|
||||
const resetLock = () => {
|
||||
lockPlay = false;
|
||||
};
|
||||
promise.then(resetLock, resetLock);
|
||||
}
|
||||
|
||||
return promise;
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
pause: () => {
|
||||
const el = ref.current;
|
||||
if (el && !lockPlay) {
|
||||
return el.pause();
|
||||
}
|
||||
},
|
||||
seek: (time: number) => {
|
||||
const el = ref.current;
|
||||
if (!el || state.duration === undefined) {
|
||||
return;
|
||||
}
|
||||
time = Math.min(state.duration, Math.max(0, time));
|
||||
el.currentTime = time;
|
||||
},
|
||||
volume: (volume: number) => {
|
||||
const el = ref.current;
|
||||
if (!el) {
|
||||
return;
|
||||
}
|
||||
volume = Math.min(1, Math.max(0, volume));
|
||||
el.volume = volume;
|
||||
setState({ volume });
|
||||
},
|
||||
mute: () => {
|
||||
const el = ref.current;
|
||||
if (!el) {
|
||||
return;
|
||||
}
|
||||
el.muted = true;
|
||||
},
|
||||
unmute: () => {
|
||||
const el = ref.current;
|
||||
if (!el) {
|
||||
return;
|
||||
}
|
||||
el.muted = false;
|
||||
},
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const el = ref.current!;
|
||||
|
||||
if (!el) {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
if (tag === 'audio') {
|
||||
console.error(
|
||||
'useAudio() ref to <audio> element is empty at mount. ' +
|
||||
'It seem you have not rendered the audio element, which it ' +
|
||||
'returns as the first argument const [audio] = useAudio(...).'
|
||||
);
|
||||
} else if (tag === 'video') {
|
||||
console.error(
|
||||
'useVideo() ref to <video> element is empty at mount. ' +
|
||||
'It seem you have not rendered the video element, which it ' +
|
||||
'returns as the first argument const [video] = useVideo(...).'
|
||||
);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
setState({
|
||||
volume: el.volume,
|
||||
muted: el.muted,
|
||||
paused: el.paused,
|
||||
});
|
||||
|
||||
// Start media, if autoPlay requested.
|
||||
if (props.autoPlay && el.paused) {
|
||||
controls.play();
|
||||
}
|
||||
}, [props.src]);
|
||||
|
||||
return [element, state, controls, ref];
|
||||
};
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
import { MutableRefObject, useCallback, useRef, useState } from 'react';
|
||||
import useUpdateEffect from './useUpdateEffect';
|
||||
import useUpdateEffect from '../useUpdateEffect';
|
||||
|
||||
type Dispatch<Action> = (action: Action) => void;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { createElement, createContext, useContext, useReducer } from 'react';
|
||||
import { createContext, createElement, useContext, useReducer } from 'react';
|
||||
|
||||
const createReducerContext = <R extends React.Reducer<any, any>>(
|
||||
reducer: R,
|
||||
@ -1,13 +1,9 @@
|
||||
const defaultMapPropsToArgs = (props) => [props];
|
||||
|
||||
const createRenderProp = (hook, mapPropsToArgs = defaultMapPropsToArgs) => {
|
||||
const RenderProp = (props) => {
|
||||
export default function createRenderProp(hook, mapPropsToArgs = defaultMapPropsToArgs) {
|
||||
return function RenderProp(props) {
|
||||
const state = hook(...mapPropsToArgs(props));
|
||||
const { children, render = children } = props;
|
||||
return render ? render(state) || null : null;
|
||||
};
|
||||
|
||||
return RenderProp;
|
||||
};
|
||||
|
||||
export default createRenderProp;
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
import { createElement, createContext, useContext, useState } from 'react';
|
||||
import { createContext, createElement, useContext, useState } from 'react';
|
||||
|
||||
const createStateContext = <T>(defaultInitialValue: T) => {
|
||||
const context = createContext<[T, React.Dispatch<React.SetStateAction<T>>] | undefined>(undefined);
|
||||
12
src/index.ts
12
src/index.ts
@ -1,7 +1,7 @@
|
||||
export { default as createMemo } from './createMemo';
|
||||
export { default as createReducerContext } from './createReducerContext';
|
||||
export { default as createReducer } from './createReducer';
|
||||
export { default as createStateContext } from './createStateContext';
|
||||
export { default as createMemo } from './factory/createMemo';
|
||||
export { default as createReducerContext } from './factory/createReducerContext';
|
||||
export { default as createReducer } from './factory/createReducer';
|
||||
export { default as createStateContext } from './factory/createStateContext';
|
||||
export { default as useAsync } from './useAsync';
|
||||
export { default as useAsyncFn } from './useAsyncFn';
|
||||
export { default as useAsyncRetry } from './useAsyncRetry';
|
||||
@ -37,7 +37,7 @@ export { default as useIntersection } from './useIntersection';
|
||||
export { default as useInterval } from './useInterval';
|
||||
export { default as useIsomorphicLayoutEffect } from './useIsomorphicLayoutEffect';
|
||||
export { default as useKey } from './useKey';
|
||||
export { default as createBreakpoint } from './createBreakpoint';
|
||||
export { default as createBreakpoint } from './factory/createBreakpoint';
|
||||
// not exported because of peer dependency
|
||||
// export { default as useKeyboardJs } from './useKeyboardJs';
|
||||
export { default as useKeyPress } from './useKeyPress';
|
||||
@ -112,5 +112,5 @@ export { default as useMeasure } from './useMeasure';
|
||||
export { useRendersCount } from './useRendersCount';
|
||||
export { useFirstMountState } from './useFirstMountState';
|
||||
export { default as useSet } from './useSet';
|
||||
export { createGlobalState } from './createGlobalState';
|
||||
export { createGlobalState } from './factory/createGlobalState';
|
||||
export { useHash } from './useHash';
|
||||
|
||||
18
src/misc/hookState.ts
Normal file
18
src/misc/hookState.ts
Normal file
@ -0,0 +1,18 @@
|
||||
export type IHookStateInitialSetter<S> = () => S;
|
||||
export type IHookStateInitAction<S> = S | IHookStateInitialSetter<S>;
|
||||
|
||||
export type IHookStateSetter<S> = ((prevState: S) => S) | (() => S);
|
||||
export type IHookStateSetAction<S> = S | IHookStateSetter<S>;
|
||||
|
||||
export type IHookStateResolvable<S> = S | IHookStateInitialSetter<S> | IHookStateSetter<S>;
|
||||
|
||||
export function resolveHookState<S>(nextState: IHookStateInitAction<S>): S;
|
||||
export function resolveHookState<S, C extends S>(nextState: IHookStateSetAction<S>, currentState?: C): S;
|
||||
export function resolveHookState<S, C extends S>(nextState: IHookStateResolvable<S>, currentState?: C): S;
|
||||
export function resolveHookState<S, C extends S>(nextState: IHookStateResolvable<S>, currentState?: C): S {
|
||||
if (typeof nextState === 'function') {
|
||||
return nextState.length ? (nextState as Function)(currentState) : (nextState as Function)();
|
||||
}
|
||||
|
||||
return nextState;
|
||||
}
|
||||
3
src/misc/isDeepEqual.ts
Normal file
3
src/misc/isDeepEqual.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import isDeepEqualReact from 'fast-deep-equal/react';
|
||||
|
||||
export default isDeepEqualReact;
|
||||
@ -1,4 +1,4 @@
|
||||
const parseTimeRanges = (ranges) => {
|
||||
export default function parseTimeRanges(ranges) {
|
||||
const result: { start: number; end: number }[] = [];
|
||||
|
||||
for (let i = 0; i < ranges.length; i++) {
|
||||
@ -9,6 +9,4 @@ const parseTimeRanges = (ranges) => {
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
export default parseTimeRanges;
|
||||
}
|
||||
3
src/misc/types.ts
Normal file
3
src/misc/types.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export type PromiseType<P extends Promise<any>> = P extends Promise<infer T> ? T : never;
|
||||
|
||||
export type FunctionReturningPromise = (...args: any[]) => Promise<any>;
|
||||
7
src/misc/util.ts
Normal file
7
src/misc/util.ts
Normal file
@ -0,0 +1,7 @@
|
||||
export const noop = () => {};
|
||||
|
||||
export const on = (obj: any, ...args: any[]) => obj.addEventListener(...args);
|
||||
|
||||
export const off = (obj: any, ...args: any[]) => obj.removeEventListener(...args);
|
||||
|
||||
export const isBrowser = typeof window === 'object';
|
||||
@ -1,10 +1,10 @@
|
||||
import { DependencyList, useEffect } from 'react';
|
||||
import useAsyncFn from './useAsyncFn';
|
||||
import { FnReturningPromise } from './util';
|
||||
import { FunctionReturningPromise } from './misc/types';
|
||||
|
||||
export { AsyncState, AsyncFnReturn } from './useAsyncFn';
|
||||
|
||||
export default function useAsync<T extends FnReturningPromise>(fn: T, deps: DependencyList = []) {
|
||||
export default function useAsync<T extends FunctionReturningPromise>(fn: T, deps: DependencyList = []) {
|
||||
const [state, callback] = useAsyncFn(fn, deps, {
|
||||
loading: true,
|
||||
});
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { DependencyList, useCallback, useState, useRef } from 'react';
|
||||
import { DependencyList, useCallback, useRef, useState } from 'react';
|
||||
import useMountedState from './useMountedState';
|
||||
import { FnReturningPromise, PromiseType } from './util';
|
||||
import { FunctionReturningPromise, PromiseType } from './misc/types';
|
||||
|
||||
export type AsyncState<T> =
|
||||
| {
|
||||
@ -24,18 +24,21 @@ export type AsyncState<T> =
|
||||
value: T;
|
||||
};
|
||||
|
||||
type StateFromFnReturningPromise<T extends FnReturningPromise> = AsyncState<PromiseType<ReturnType<T>>>;
|
||||
type StateFromFunctionReturningPromise<T extends FunctionReturningPromise> = AsyncState<PromiseType<ReturnType<T>>>;
|
||||
|
||||
export type AsyncFnReturn<T extends FnReturningPromise = FnReturningPromise> = [StateFromFnReturningPromise<T>, T];
|
||||
export type AsyncFnReturn<T extends FunctionReturningPromise = FunctionReturningPromise> = [
|
||||
StateFromFunctionReturningPromise<T>,
|
||||
T
|
||||
];
|
||||
|
||||
export default function useAsyncFn<T extends FnReturningPromise>(
|
||||
export default function useAsyncFn<T extends FunctionReturningPromise>(
|
||||
fn: T,
|
||||
deps: DependencyList = [],
|
||||
initialState: StateFromFnReturningPromise<T> = { loading: false }
|
||||
initialState: StateFromFunctionReturningPromise<T> = { loading: false }
|
||||
): AsyncFnReturn<T> {
|
||||
const lastCallId = useRef(0);
|
||||
const isMounted = useMountedState();
|
||||
const [state, set] = useState<StateFromFnReturningPromise<T>>(initialState);
|
||||
const [state, set] = useState<StateFromFunctionReturningPromise<T>>(initialState);
|
||||
|
||||
const callback = useCallback((...args: Parameters<T>): ReturnType<T> => {
|
||||
const callId = ++lastCallId.current;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import createHTMLMediaHook from './util/createHTMLMediaHook';
|
||||
import createHTMLMediaHook from './factory/createHTMLMediaHook';
|
||||
|
||||
const useAudio = createHTMLMediaHook('audio');
|
||||
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { off, on, isDeepEqual } from './util';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { off, on } from './misc/util';
|
||||
import isDeepEqual from './misc/isDeepEqual';
|
||||
|
||||
export interface BatteryState {
|
||||
charging: boolean;
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { useCallback, useEffect } from 'react';
|
||||
import { off, on } from './misc/util';
|
||||
|
||||
const useBeforeUnload = (enabled: boolean | (() => boolean) = true, message?: string) => {
|
||||
const handler = useCallback(
|
||||
@ -25,9 +26,9 @@ const useBeforeUnload = (enabled: boolean | (() => boolean) = true, message?: st
|
||||
return;
|
||||
}
|
||||
|
||||
window.addEventListener('beforeunload', handler);
|
||||
on(window, 'beforeunload', handler);
|
||||
|
||||
return () => window.removeEventListener('beforeunload', handler);
|
||||
return () => off(window, 'beforeunload', handler);
|
||||
}, [enabled, handler]);
|
||||
};
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { RefObject, useEffect, useRef } from 'react';
|
||||
import { off, on } from './util';
|
||||
import { off, on } from './misc/util';
|
||||
|
||||
const defaultEvents = ['mousedown', 'touchstart'];
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { useState, useCallback } from 'react';
|
||||
import { useCallback, useState } from 'react';
|
||||
import Cookies from 'js-cookie';
|
||||
|
||||
const useCookie = (
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
import { useMemo } from 'react';
|
||||
import useGetSet from './useGetSet';
|
||||
import { HookState, InitialHookState, resolveHookState } from './util/resolveHookState';
|
||||
import { IHookStateInitAction, IHookStateSetAction, resolveHookState } from './misc/hookState';
|
||||
|
||||
export interface CounterActions {
|
||||
inc: (delta?: number) => void;
|
||||
dec: (delta?: number) => void;
|
||||
get: () => number;
|
||||
set: (value: HookState<number>) => void;
|
||||
reset: (value?: HookState<number>) => void;
|
||||
set: (value: IHookStateSetAction<number>) => void;
|
||||
reset: (value?: IHookStateSetAction<number>) => void;
|
||||
}
|
||||
|
||||
export default function useCounter(
|
||||
initialValue: InitialHookState<number> = 0,
|
||||
initialValue: IHookStateInitAction<number> = 0,
|
||||
max: number | null = null,
|
||||
min: number | null = null
|
||||
): [number, CounterActions] {
|
||||
@ -36,7 +36,7 @@ export default function useCounter(
|
||||
return [
|
||||
get(),
|
||||
useMemo(() => {
|
||||
const set = (newState: HookState<number>) => {
|
||||
const set = (newState: IHookStateSetAction<number>) => {
|
||||
const prevState = get();
|
||||
let rState = resolveHookState(newState, prevState);
|
||||
|
||||
@ -55,7 +55,7 @@ export default function useCounter(
|
||||
return {
|
||||
get,
|
||||
set,
|
||||
inc: (delta: HookState<number> = 1) => {
|
||||
inc: (delta: IHookStateSetAction<number> = 1) => {
|
||||
const rDelta = resolveHookState(delta, get());
|
||||
|
||||
if (typeof rDelta !== 'number') {
|
||||
@ -64,7 +64,7 @@ export default function useCounter(
|
||||
|
||||
set((num: number) => num + rDelta);
|
||||
},
|
||||
dec: (delta: HookState<number> = 1) => {
|
||||
dec: (delta: IHookStateSetAction<number> = 1) => {
|
||||
const rDelta = resolveHookState(delta, get());
|
||||
|
||||
if (typeof rDelta !== 'number') {
|
||||
@ -73,13 +73,14 @@ export default function useCounter(
|
||||
|
||||
set((num: number) => num - rDelta);
|
||||
},
|
||||
reset: (value: HookState<number> = init) => {
|
||||
reset: (value: IHookStateSetAction<number> = init) => {
|
||||
const rValue = resolveHookState(value, get());
|
||||
|
||||
if (typeof rValue !== 'number') {
|
||||
console.error('value has to be a number or function returning a number, got ' + typeof rValue);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
init = rValue;
|
||||
set(rValue);
|
||||
},
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { DependencyList, EffectCallback } from 'react';
|
||||
import { isDeepEqual } from './util';
|
||||
import useCustomCompareEffect from './useCustomCompareEffect';
|
||||
import isDeepEqual from './misc/isDeepEqual';
|
||||
|
||||
const isPrimitive = (val: any) => val !== Object(val);
|
||||
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { useState, useMemo, useCallback, useEffect } from 'react';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { noop, off, on } from './misc/util';
|
||||
|
||||
export interface DropAreaState {
|
||||
over: boolean;
|
||||
@ -18,8 +19,6 @@ export interface DropAreaOptions {
|
||||
onUri?: (url: string, event?) => void;
|
||||
}
|
||||
|
||||
const noop = () => {};
|
||||
|
||||
const createProcess = (options: DropAreaOptions) => (dataTransfer: DataTransfer, event) => {
|
||||
const uri = dataTransfer.getData('text/uri-list');
|
||||
|
||||
@ -75,22 +74,22 @@ const useDrop = (options: DropAreaOptions = {}, args = []): DropAreaState => {
|
||||
process(event.clipboardData, event);
|
||||
};
|
||||
|
||||
document.addEventListener('dragover', onDragOver);
|
||||
document.addEventListener('dragenter', onDragEnter);
|
||||
document.addEventListener('dragleave', onDragLeave);
|
||||
document.addEventListener('dragexit', onDragExit);
|
||||
document.addEventListener('drop', onDrop);
|
||||
on(document, 'dragover', onDragOver);
|
||||
on(document, 'dragenter', onDragEnter);
|
||||
on(document, 'dragleave', onDragLeave);
|
||||
on(document, 'dragexit', onDragExit);
|
||||
on(document, 'drop', onDrop);
|
||||
if (onText) {
|
||||
document.addEventListener('paste', onPaste);
|
||||
on(document, 'paste', onPaste);
|
||||
}
|
||||
|
||||
return () => {
|
||||
document.removeEventListener('dragover', onDragOver);
|
||||
document.removeEventListener('dragenter', onDragEnter);
|
||||
document.removeEventListener('dragleave', onDragLeave);
|
||||
document.removeEventListener('dragexit', onDragExit);
|
||||
document.removeEventListener('drop', onDrop);
|
||||
document.removeEventListener('paste', onPaste);
|
||||
off(document, 'dragover', onDragOver);
|
||||
off(document, 'dragenter', onDragEnter);
|
||||
off(document, 'dragleave', onDragLeave);
|
||||
off(document, 'dragexit', onDragExit);
|
||||
off(document, 'drop', onDrop);
|
||||
off(document, 'paste', onPaste);
|
||||
};
|
||||
}, [process, ...args]);
|
||||
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { useMemo, useState } from 'react';
|
||||
import useMountedState from './useMountedState';
|
||||
import { noop } from './misc/util';
|
||||
|
||||
export interface DropAreaState {
|
||||
over: boolean;
|
||||
@ -19,7 +20,6 @@ export interface DropAreaOptions {
|
||||
onUri?: (url: string, event?) => void;
|
||||
}
|
||||
|
||||
const noop = () => {};
|
||||
/*
|
||||
const defaultState: DropAreaState = {
|
||||
over: false,
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
import {
|
||||
forwardRef,
|
||||
useRef,
|
||||
useEffect,
|
||||
MutableRefObject,
|
||||
ForwardRefExoticComponent,
|
||||
MutableRefObject,
|
||||
PropsWithChildren,
|
||||
PropsWithoutRef,
|
||||
RefAttributes,
|
||||
RefForwardingComponent,
|
||||
PropsWithChildren,
|
||||
useEffect,
|
||||
useRef,
|
||||
} from 'react';
|
||||
|
||||
export default function useEnsuredForwardedRef<T>(forwardedRef: MutableRefObject<T>): MutableRefObject<T> {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
|
||||
const useError = (): ((err: Error) => void) => {
|
||||
const [error, setError] = useState<Error | null>(null);
|
||||
|
||||
@ -1,19 +1,21 @@
|
||||
import { useEffect } from 'react';
|
||||
import { isClient } from './util';
|
||||
import { isBrowser, off, on } from './misc/util';
|
||||
|
||||
export interface ListenerType1 {
|
||||
addEventListener(name: string, handler: (event?: any) => void, ...args: any[]);
|
||||
|
||||
removeEventListener(name: string, handler: (event?: any) => void, ...args: any[]);
|
||||
}
|
||||
|
||||
export interface ListenerType2 {
|
||||
on(name: string, handler: (event?: any) => void, ...args: any[]);
|
||||
|
||||
off(name: string, handler: (event?: any) => void, ...args: any[]);
|
||||
}
|
||||
|
||||
export type UseEventTarget = ListenerType1 | ListenerType2;
|
||||
|
||||
const defaultTarget = isClient ? window : null;
|
||||
const defaultTarget = isBrowser ? window : null;
|
||||
|
||||
const isListenerType1 = (target: any): target is ListenerType1 => {
|
||||
return !!target.addEventListener;
|
||||
@ -38,13 +40,13 @@ const useEvent = <T extends UseEventTarget>(
|
||||
return;
|
||||
}
|
||||
if (isListenerType1(target)) {
|
||||
target.addEventListener(name, handler, options);
|
||||
on(target, name, handler, options);
|
||||
} else if (isListenerType2(target)) {
|
||||
target.on(name, handler, options);
|
||||
}
|
||||
return () => {
|
||||
if (isListenerType1(target)) {
|
||||
target.removeEventListener(name, handler, options);
|
||||
off(target, name, handler, options);
|
||||
} else if (isListenerType2(target)) {
|
||||
target.off(name, handler, options);
|
||||
}
|
||||
|
||||
@ -1,20 +1,19 @@
|
||||
import { RefObject, useState } from 'react';
|
||||
import screenfull from 'screenfull';
|
||||
import useIsomorphicLayoutEffect from './useIsomorphicLayoutEffect';
|
||||
import { noop, off, on } from './misc/util';
|
||||
|
||||
export interface FullScreenOptions {
|
||||
video?: RefObject<HTMLVideoElement & { webkitEnterFullscreen?: () => void; webkitExitFullscreen?: () => void }>;
|
||||
onClose?: (error?: Error) => void;
|
||||
}
|
||||
|
||||
const noop = () => {};
|
||||
|
||||
const useFullscreen = (ref: RefObject<Element>, on: boolean, options: FullScreenOptions = {}): boolean => {
|
||||
const useFullscreen = (ref: RefObject<Element>, enabled: boolean, options: FullScreenOptions = {}): boolean => {
|
||||
const { video, onClose = noop } = options;
|
||||
const [isFullscreen, setIsFullscreen] = useState(on);
|
||||
const [isFullscreen, setIsFullscreen] = useState(enabled);
|
||||
|
||||
useIsomorphicLayoutEffect(() => {
|
||||
if (!on) {
|
||||
if (!enabled) {
|
||||
return;
|
||||
}
|
||||
if (!ref.current) {
|
||||
@ -22,7 +21,9 @@ const useFullscreen = (ref: RefObject<Element>, on: boolean, options: FullScreen
|
||||
}
|
||||
|
||||
const onWebkitEndFullscreen = () => {
|
||||
video!.current!.removeEventListener('webkitendfullscreen', onWebkitEndFullscreen);
|
||||
if (video?.current) {
|
||||
off(video.current, 'webkitendfullscreen', onWebkitEndFullscreen);
|
||||
}
|
||||
onClose();
|
||||
};
|
||||
|
||||
@ -47,7 +48,7 @@ const useFullscreen = (ref: RefObject<Element>, on: boolean, options: FullScreen
|
||||
screenfull.on('change', onChange);
|
||||
} else if (video && video.current && video.current.webkitEnterFullscreen) {
|
||||
video.current.webkitEnterFullscreen();
|
||||
video.current.addEventListener('webkitendfullscreen', onWebkitEndFullscreen);
|
||||
on(video.current, 'webkitendfullscreen', onWebkitEndFullscreen);
|
||||
setIsFullscreen(true);
|
||||
} else {
|
||||
onClose();
|
||||
@ -62,11 +63,11 @@ const useFullscreen = (ref: RefObject<Element>, on: boolean, options: FullScreen
|
||||
screenfull.exit();
|
||||
} catch {}
|
||||
} else if (video && video.current && video.current.webkitExitFullscreen) {
|
||||
video.current.removeEventListener('webkitendfullscreen', onWebkitEndFullscreen);
|
||||
off(video.current, 'webkitendfullscreen', onWebkitEndFullscreen);
|
||||
video.current.webkitExitFullscreen();
|
||||
}
|
||||
};
|
||||
}, [on, video, ref]);
|
||||
}, [enabled, video, ref]);
|
||||
|
||||
return isFullscreen;
|
||||
};
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
import { Dispatch, useMemo, useRef } from 'react';
|
||||
import useUpdate from './useUpdate';
|
||||
import { HookState, InitialHookState, resolveHookState } from './util/resolveHookState';
|
||||
import { IHookStateInitAction, IHookStateSetAction, resolveHookState } from './misc/hookState';
|
||||
|
||||
export default function useGetSet<S>(initialState: InitialHookState<S>): [() => S, Dispatch<HookState<S>>] {
|
||||
export default function useGetSet<S>(
|
||||
initialState: IHookStateInitAction<S>
|
||||
): [get: () => S, set: Dispatch<IHookStateSetAction<S>>] {
|
||||
const state = useRef(resolveHookState(initialState));
|
||||
const update = useUpdate();
|
||||
|
||||
return useMemo(
|
||||
() => [
|
||||
// get
|
||||
() => state.current as S,
|
||||
// set
|
||||
(newState: HookState<S>) => {
|
||||
(newState: IHookStateSetAction<S>) => {
|
||||
state.current = resolveHookState(newState, state.current);
|
||||
update();
|
||||
},
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { setHarmonicInterval, clearHarmonicInterval } from 'set-harmonic-interval';
|
||||
import { clearHarmonicInterval, setHarmonicInterval } from 'set-harmonic-interval';
|
||||
|
||||
const useHarmonicIntervalFn = (fn: Function, delay: number | null = 0) => {
|
||||
const latestCallback = useRef<Function>(() => {});
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { useState, useCallback } from 'react';
|
||||
import { useCallback, useState } from 'react';
|
||||
import useLifecycles from './useLifecycles';
|
||||
import { off, on } from './misc/util';
|
||||
|
||||
/**
|
||||
* read and write url hash, response to url hash change
|
||||
@ -13,10 +14,10 @@ export const useHash = () => {
|
||||
|
||||
useLifecycles(
|
||||
() => {
|
||||
window.addEventListener('hashchange', onHashChange);
|
||||
on(window, 'hashchange', onHashChange);
|
||||
},
|
||||
() => {
|
||||
window.removeEventListener('hashchange', onHashChange);
|
||||
off(window, 'hashchange', onHashChange);
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
import * as React from 'react';
|
||||
import { noop } from './misc/util';
|
||||
|
||||
const { useState } = React;
|
||||
|
||||
const noop = () => {};
|
||||
|
||||
export type Element = ((state: boolean) => React.ReactElement<any>) | React.ReactElement<any>;
|
||||
|
||||
const useHover = (element: Element): [React.ReactElement<any>, boolean] => {
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { RefObject, useEffect, useState } from 'react';
|
||||
import { off, on } from './misc/util';
|
||||
|
||||
// kudos: https://usehooks.com/
|
||||
const useHoverDirty = (ref: RefObject<Element>, enabled: boolean = true) => {
|
||||
@ -15,8 +16,8 @@ const useHoverDirty = (ref: RefObject<Element>, enabled: boolean = true) => {
|
||||
const onMouseOut = () => setValue(false);
|
||||
|
||||
if (enabled && ref && ref.current) {
|
||||
ref.current.addEventListener('mouseover', onMouseOver);
|
||||
ref.current.addEventListener('mouseout', onMouseOut);
|
||||
on(ref.current, 'mouseover', onMouseOver);
|
||||
on(ref.current, 'mouseout', onMouseOut);
|
||||
}
|
||||
|
||||
// fixes react-hooks/exhaustive-deps warning about stale ref elements
|
||||
@ -24,8 +25,8 @@ const useHoverDirty = (ref: RefObject<Element>, enabled: boolean = true) => {
|
||||
|
||||
return () => {
|
||||
if (enabled && current) {
|
||||
current.removeEventListener('mouseover', onMouseOver);
|
||||
current.removeEventListener('mouseout', onMouseOut);
|
||||
off(current, 'mouseover', onMouseOver);
|
||||
off(current, 'mouseout', onMouseOut);
|
||||
}
|
||||
};
|
||||
}, [enabled, ref]);
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { throttle } from 'throttle-debounce';
|
||||
import { off, on } from './util';
|
||||
import { off, on } from './misc/util';
|
||||
|
||||
const defaultEvents = ['mousemove', 'mousedown', 'resize', 'keydown', 'touchstart', 'wheel'];
|
||||
const oneMinute = 60e3;
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { DependencyList, useMemo } from 'react';
|
||||
import useEvent, { UseEventTarget } from './useEvent';
|
||||
import { noop } from './misc/util';
|
||||
|
||||
export type KeyPredicate = (event: KeyboardEvent) => boolean;
|
||||
export type KeyFilter = null | undefined | string | ((event: KeyboardEvent) => boolean);
|
||||
@ -11,7 +12,6 @@ export interface UseKeyOptions {
|
||||
options?: any;
|
||||
}
|
||||
|
||||
const noop = () => {};
|
||||
const createKeyPredicate = (keyFilter: KeyFilter): KeyPredicate =>
|
||||
typeof keyFilter === 'function'
|
||||
? keyFilter
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
import { useMemo, useRef } from 'react';
|
||||
import useUpdate from './useUpdate';
|
||||
import { InitialHookState, ResolvableHookState, resolveHookState } from './util/resolveHookState';
|
||||
import { IHookStateInitAction, IHookStateSetAction, resolveHookState } from './misc/hookState';
|
||||
|
||||
export interface ListActions<T> {
|
||||
/**
|
||||
* @description Set new list instead old one
|
||||
*/
|
||||
set: (newList: ResolvableHookState<T[]>) => void;
|
||||
set: (newList: IHookStateSetAction<T[]>) => void;
|
||||
/**
|
||||
* @description Add item(s) at the end of list
|
||||
*/
|
||||
@ -62,13 +62,13 @@ export interface ListActions<T> {
|
||||
reset: () => void;
|
||||
}
|
||||
|
||||
function useList<T>(initialList: InitialHookState<T[]> = []): [T[], ListActions<T>] {
|
||||
function useList<T>(initialList: IHookStateInitAction<T[]> = []): [T[], ListActions<T>] {
|
||||
const list = useRef(resolveHookState(initialList));
|
||||
const update = useUpdate();
|
||||
|
||||
const actions = useMemo<ListActions<T>>(() => {
|
||||
const a = {
|
||||
set: (newList: ResolvableHookState<T[]>) => {
|
||||
set: (newList: IHookStateSetAction<T[]>) => {
|
||||
list.current = resolveHookState(newList, list.current);
|
||||
update();
|
||||
},
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { useState, useCallback, Dispatch, SetStateAction } from 'react';
|
||||
import { isClient } from './util';
|
||||
import { Dispatch, SetStateAction, useCallback, useState } from 'react';
|
||||
import { isBrowser, noop } from './misc/util';
|
||||
|
||||
type parserOptions<T> =
|
||||
| {
|
||||
@ -11,14 +11,12 @@ type parserOptions<T> =
|
||||
deserializer: (value: string) => T;
|
||||
};
|
||||
|
||||
const noop = () => {};
|
||||
|
||||
const useLocalStorage = <T>(
|
||||
key: string,
|
||||
initialValue?: T,
|
||||
options?: parserOptions<T>
|
||||
): [T | undefined, Dispatch<SetStateAction<T | undefined>>, () => void] => {
|
||||
if (!isClient) {
|
||||
if (!isBrowser) {
|
||||
return [initialValue as T, noop, noop];
|
||||
}
|
||||
if (!key) {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { isClient, off, on } from './util';
|
||||
import { isBrowser, off, on } from './misc/util';
|
||||
|
||||
const patchHistoryMethod = (method) => {
|
||||
const history = window.history;
|
||||
@ -17,7 +17,7 @@ const patchHistoryMethod = (method) => {
|
||||
};
|
||||
};
|
||||
|
||||
if (isClient) {
|
||||
if (isBrowser) {
|
||||
patchHistoryMethod('pushState');
|
||||
patchHistoryMethod('replaceState');
|
||||
}
|
||||
@ -87,4 +87,4 @@ const useLocationBrowser = (): LocationSensorState => {
|
||||
|
||||
const hasEventConstructor = typeof Event === 'function';
|
||||
|
||||
export default isClient && hasEventConstructor ? useLocationBrowser : useLocationServer;
|
||||
export default isBrowser && hasEventConstructor ? useLocationBrowser : useLocationServer;
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { RefObject, useEffect, useRef } from 'react';
|
||||
import { isBrowser, off, on } from './misc/util';
|
||||
|
||||
export function getClosestBody(el: Element | HTMLElement | HTMLIFrameElement | null): HTMLElement | null {
|
||||
if (!el) {
|
||||
@ -31,10 +32,7 @@ export interface BodyInfoItem {
|
||||
}
|
||||
|
||||
const isIosDevice =
|
||||
typeof window !== 'undefined' &&
|
||||
window.navigator &&
|
||||
window.navigator.platform &&
|
||||
/iP(ad|hone|od)/.test(window.navigator.platform);
|
||||
isBrowser && window.navigator && window.navigator.platform && /iP(ad|hone|od)/.test(window.navigator.platform);
|
||||
|
||||
const bodies: Map<HTMLElement, BodyInfoItem> = new Map();
|
||||
|
||||
@ -54,7 +52,7 @@ export default !doc
|
||||
bodies.set(body, { counter: 1, initialOverflow: body.style.overflow });
|
||||
if (isIosDevice) {
|
||||
if (!documentListenerAdded) {
|
||||
document.addEventListener('touchmove', preventDefault, { passive: false });
|
||||
on(document, 'touchmove', preventDefault, { passive: false });
|
||||
|
||||
documentListenerAdded = true;
|
||||
}
|
||||
@ -75,7 +73,7 @@ export default !doc
|
||||
body.ontouchmove = null;
|
||||
|
||||
if (documentListenerAdded) {
|
||||
document.removeEventListener('touchmove', preventDefault);
|
||||
off(document, 'touchmove', preventDefault);
|
||||
documentListenerAdded = false;
|
||||
}
|
||||
} else {
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { useCallback, useRef } from 'react';
|
||||
import { off, on } from './misc/util';
|
||||
|
||||
interface Options {
|
||||
isPreventDefault?: boolean;
|
||||
@ -28,7 +29,7 @@ const useLongPress = (
|
||||
(event: TouchEvent | MouseEvent) => {
|
||||
// prevent ghost click on mobile devices
|
||||
if (isPreventDefault && event.target) {
|
||||
event.target.addEventListener('touchend', preventDefault, { passive: false });
|
||||
on(event.target, 'touchend', preventDefault, { passive: false });
|
||||
target.current = event.target;
|
||||
}
|
||||
timeout.current = setTimeout(() => callback(event), delay);
|
||||
@ -41,7 +42,7 @@ const useLongPress = (
|
||||
timeout.current && clearTimeout(timeout.current);
|
||||
|
||||
if (isPreventDefault && target.current) {
|
||||
target.current.removeEventListener('touchend', preventDefault);
|
||||
off(target.current, 'touchend', preventDefault);
|
||||
}
|
||||
}, [isPreventDefault]);
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { useState, useMemo, useCallback } from 'react';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
|
||||
export interface StableActions<T extends object> {
|
||||
set: <K extends keyof T>(key: K, value: T[K]) => void;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { useState, useMemo } from 'react';
|
||||
import { useMemo, useState } from 'react';
|
||||
import useIsomorphicLayoutEffect from './useIsomorphicLayoutEffect';
|
||||
import { isClient } from './util';
|
||||
import { isBrowser, noop } from './misc/util';
|
||||
|
||||
export type UseMeasureRect = Pick<
|
||||
DOMRectReadOnly,
|
||||
@ -20,7 +20,7 @@ const defaultState: UseMeasureRect = {
|
||||
right: 0,
|
||||
};
|
||||
|
||||
const useMeasure = <E extends HTMLElement = HTMLElement>(): UseMeasureResult<E> => {
|
||||
function useMeasure<E extends HTMLElement = HTMLElement>(): UseMeasureResult<E> {
|
||||
const [element, ref] = useState<E | null>(null);
|
||||
const [rect, setRect] = useState<UseMeasureRect>(defaultState);
|
||||
|
||||
@ -44,8 +44,8 @@ const useMeasure = <E extends HTMLElement = HTMLElement>(): UseMeasureResult<E>
|
||||
}, [element]);
|
||||
|
||||
return [ref, rect];
|
||||
};
|
||||
}
|
||||
|
||||
const useMeasureMock: typeof useMeasure = () => [() => {}, defaultState];
|
||||
|
||||
export default isClient && !!(window as any).ResizeObserver ? useMeasure : useMeasureMock;
|
||||
export default isBrowser && typeof (window as any).ResizeObserver !== 'undefined'
|
||||
? useMeasure
|
||||
: () => [noop, defaultState];
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { useState, useEffect, useRef, RefObject } from 'react';
|
||||
import { RefObject, useEffect, useRef, useState } from 'react';
|
||||
import ResizeObserver from 'resize-observer-polyfill';
|
||||
|
||||
export interface ContentRect {
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { isClient } from './util';
|
||||
import { isBrowser } from './misc/util';
|
||||
|
||||
const useMedia = (query: string, defaultState: boolean = false) => {
|
||||
const [state, setState] = useState(isClient ? () => window.matchMedia(query).matches : defaultState);
|
||||
const [state, setState] = useState(isBrowser ? () => window.matchMedia(query).matches : defaultState);
|
||||
|
||||
useEffect(() => {
|
||||
let mounted = true;
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { off, on } from './util';
|
||||
|
||||
const noop = () => {};
|
||||
import { noop, off, on } from './misc/util';
|
||||
|
||||
const useMediaDevices = () => {
|
||||
const [state, setState] = useState({});
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { useMemo, useReducer, Reducer } from 'react';
|
||||
import { Reducer, useMemo, useReducer } from 'react';
|
||||
|
||||
type Action = {
|
||||
type: string;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { off, on } from './util';
|
||||
import { off, on } from './misc/util';
|
||||
|
||||
export interface MotionSensorState {
|
||||
acceleration: {
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { RefObject, useEffect } from 'react';
|
||||
|
||||
import useRafState from './useRafState';
|
||||
import { off, on } from './misc/util';
|
||||
|
||||
export interface State {
|
||||
docX: number;
|
||||
@ -53,10 +54,10 @@ const useMouse = (ref: RefObject<Element>): State => {
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('mousemove', moveHandler);
|
||||
on(document, 'mousemove', moveHandler);
|
||||
|
||||
return () => {
|
||||
document.removeEventListener('mousemove', moveHandler);
|
||||
off(document, 'mousemove', moveHandler);
|
||||
};
|
||||
}, [ref]);
|
||||
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { off, on } from './misc/util';
|
||||
|
||||
export default () => {
|
||||
const [mouseWheelScrolled, setMouseWheelScrolled] = useState(0);
|
||||
@ -6,8 +7,8 @@ export default () => {
|
||||
const updateScroll = (e: MouseWheelEvent) => {
|
||||
setMouseWheelScrolled(e.deltaY + mouseWheelScrolled);
|
||||
};
|
||||
window.addEventListener('wheel', updateScroll, false);
|
||||
return () => window.removeEventListener('wheel', updateScroll);
|
||||
on(window, 'wheel', updateScroll, false);
|
||||
return () => off(window, 'wheel', updateScroll);
|
||||
});
|
||||
return mouseWheelScrolled;
|
||||
};
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { off, on } from './util';
|
||||
import { off, on } from './misc/util';
|
||||
|
||||
export interface NetworkState {
|
||||
online?: boolean;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { off, on } from './util';
|
||||
import { off, on } from './misc/util';
|
||||
|
||||
export interface OrientationState {
|
||||
angle: number;
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { useEffect } from 'react';
|
||||
import { off, on } from './misc/util';
|
||||
|
||||
const usePageLeave = (onPageLeave, args = []) => {
|
||||
useEffect(() => {
|
||||
@ -14,9 +15,9 @@ const usePageLeave = (onPageLeave, args = []) => {
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('mouseout', handler);
|
||||
on(document, 'mouseout', handler);
|
||||
return () => {
|
||||
document.removeEventListener('mouseout', handler);
|
||||
off(document, 'mouseout', handler);
|
||||
};
|
||||
}, args);
|
||||
};
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { off, on } from './util';
|
||||
import { noop, off, on } from './misc/util';
|
||||
|
||||
type PermissionDesc =
|
||||
| PermissionDescriptor
|
||||
@ -9,8 +9,6 @@ type PermissionDesc =
|
||||
|
||||
type State = PermissionState | '';
|
||||
|
||||
const noop = () => {};
|
||||
|
||||
const usePermission = (permissionDesc: PermissionDesc): State => {
|
||||
let mounted = true;
|
||||
let permissionStatus: PermissionStatus | null = null;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { useRef, useState, useCallback, Dispatch, SetStateAction } from 'react';
|
||||
import { Dispatch, SetStateAction, useCallback, useRef, useState } from 'react';
|
||||
|
||||
import useUnmount from './useUnmount';
|
||||
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
import { useState, useEffect, useRef, FC, cloneElement } from 'react';
|
||||
import { cloneElement, FC, useEffect, useRef, useState } from 'react';
|
||||
import { render } from 'react-universal-interface';
|
||||
import useLatest from './useLatest';
|
||||
|
||||
const noop = () => {};
|
||||
import { noop, off, on } from './misc/util';
|
||||
|
||||
export interface ScratchSensorParams {
|
||||
disabled?: boolean;
|
||||
@ -81,10 +80,10 @@ const useScratch = (params: ScratchSensorParams = {}): [(el: HTMLElement | null)
|
||||
refState.current = { ...refState.current, isScratching: false };
|
||||
(paramsRef.current.onScratchEnd || noop)(refState.current);
|
||||
setState({ isScratching: false });
|
||||
window.removeEventListener('mousemove', onMouseMove);
|
||||
window.removeEventListener('touchmove', onTouchMove);
|
||||
window.removeEventListener('mouseup', onMouseUp);
|
||||
window.removeEventListener('touchend', onTouchEnd);
|
||||
off(window, 'mousemove', onMouseMove);
|
||||
off(window, 'touchmove', onTouchMove);
|
||||
off(window, 'mouseup', onMouseUp);
|
||||
off(window, 'touchend', onTouchEnd);
|
||||
};
|
||||
|
||||
onMouseUp = stopScratching;
|
||||
@ -116,10 +115,10 @@ const useScratch = (params: ScratchSensorParams = {}): [(el: HTMLElement | null)
|
||||
refState.current = newState;
|
||||
(paramsRef.current.onScratchStart || noop)(newState);
|
||||
setState(newState);
|
||||
window.addEventListener('mousemove', onMouseMove);
|
||||
window.addEventListener('touchmove', onTouchMove);
|
||||
window.addEventListener('mouseup', onMouseUp);
|
||||
window.addEventListener('touchend', onTouchEnd);
|
||||
on(window, 'mousemove', onMouseMove);
|
||||
on(window, 'touchmove', onTouchMove);
|
||||
on(window, 'mouseup', onMouseUp);
|
||||
on(window, 'touchend', onTouchEnd);
|
||||
};
|
||||
|
||||
const onMouseDown = (event) => {
|
||||
@ -132,16 +131,16 @@ const useScratch = (params: ScratchSensorParams = {}): [(el: HTMLElement | null)
|
||||
startScratching(event.changedTouches[0].pageX, event.changedTouches[0].pageY);
|
||||
};
|
||||
|
||||
el.addEventListener('mousedown', onMouseDown);
|
||||
el.addEventListener('touchstart', onTouchStart);
|
||||
on(el, 'mousedown', onMouseDown);
|
||||
on(el, 'touchstart', onTouchStart);
|
||||
|
||||
return () => {
|
||||
el.removeEventListener('mousedown', onMouseDown);
|
||||
el.removeEventListener('touchstart', onTouchStart);
|
||||
window.removeEventListener('mousemove', onMouseMove);
|
||||
window.removeEventListener('touchmove', onTouchMove);
|
||||
window.removeEventListener('mouseup', onMouseUp);
|
||||
window.removeEventListener('touchend', onTouchEnd);
|
||||
off(el, 'mousedown', onMouseDown);
|
||||
off(el, 'touchstart', onTouchStart);
|
||||
off(window, 'mousemove', onMouseMove);
|
||||
off(window, 'touchmove', onTouchMove);
|
||||
off(window, 'mouseup', onMouseUp);
|
||||
off(window, 'touchend', onTouchEnd);
|
||||
|
||||
if (refAnimationFrame.current) cancelAnimationFrame(refAnimationFrame.current);
|
||||
refAnimationFrame.current = null;
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { RefObject, useEffect } from 'react';
|
||||
|
||||
import useRafState from './useRafState';
|
||||
import { off, on } from './misc/util';
|
||||
|
||||
export interface State {
|
||||
x: number;
|
||||
@ -30,7 +31,7 @@ const useScroll = (ref: RefObject<HTMLElement>): State => {
|
||||
};
|
||||
|
||||
if (ref.current) {
|
||||
ref.current.addEventListener('scroll', handler, {
|
||||
on(ref.current, 'scroll', handler, {
|
||||
capture: false,
|
||||
passive: true,
|
||||
});
|
||||
@ -38,7 +39,7 @@ const useScroll = (ref: RefObject<HTMLElement>): State => {
|
||||
|
||||
return () => {
|
||||
if (ref.current) {
|
||||
ref.current.removeEventListener('scroll', handler);
|
||||
off(ref.current, 'scroll', handler);
|
||||
}
|
||||
};
|
||||
}, [ref]);
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { RefObject, useEffect, useState } from 'react';
|
||||
import { off, on } from './misc/util';
|
||||
|
||||
const useScrolling = (ref: RefObject<HTMLElement>): boolean => {
|
||||
const [scrolling, setScrolling] = useState<boolean>(false);
|
||||
@ -17,10 +18,10 @@ const useScrolling = (ref: RefObject<HTMLElement>): boolean => {
|
||||
scrollingTimeout = setTimeout(() => handleScrollEnd(), 150);
|
||||
};
|
||||
|
||||
ref.current.addEventListener('scroll', handleScroll, false);
|
||||
on(ref.current, 'scroll', handleScroll, false);
|
||||
return () => {
|
||||
if (ref.current) {
|
||||
ref.current.removeEventListener('scroll', handleScroll, false);
|
||||
off(ref.current, 'scroll', handleScroll, false);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { isBrowser, off, on } from './misc/util';
|
||||
|
||||
const getValue = (search: string, param: string) => new URLSearchParams(search).get(param);
|
||||
|
||||
@ -13,14 +14,14 @@ const useSearchParam: UseQueryParam = (param) => {
|
||||
setValue(getValue(location.search, param));
|
||||
};
|
||||
|
||||
window.addEventListener('popstate', onChange);
|
||||
window.addEventListener('pushstate', onChange);
|
||||
window.addEventListener('replacestate', onChange);
|
||||
on(window, 'popstate', onChange);
|
||||
on(window, 'pushstate', onChange);
|
||||
on(window, 'replacestate', onChange);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('popstate', onChange);
|
||||
window.removeEventListener('pushstate', onChange);
|
||||
window.removeEventListener('replacestate', onChange);
|
||||
off(window, 'popstate', onChange);
|
||||
off(window, 'pushstate', onChange);
|
||||
off(window, 'replacestate', onChange);
|
||||
};
|
||||
}, []);
|
||||
|
||||
@ -29,4 +30,4 @@ const useSearchParam: UseQueryParam = (param) => {
|
||||
|
||||
const useSearchParamServer = () => null;
|
||||
|
||||
export default typeof window === 'object' ? useSearchParam : useSearchParamServer;
|
||||
export default isBrowser ? useSearchParam : useSearchParamServer;
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { isClient } from './util';
|
||||
import { isBrowser } from './misc/util';
|
||||
|
||||
const useSessionStorage = <T>(key: string, initialValue?: T, raw?: boolean): [T, (value: T) => void] => {
|
||||
if (!isClient) {
|
||||
if (!isBrowser) {
|
||||
return [initialValue as T, () => {}];
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { useState, useMemo, useCallback } from 'react';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
|
||||
export interface StableActions<K> {
|
||||
add: (key: K) => void;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import { isClient } from './util';
|
||||
import { isBrowser, off, on } from './misc/util';
|
||||
|
||||
const { useState, useEffect, useRef } = React;
|
||||
|
||||
@ -16,7 +16,7 @@ const useSize = (
|
||||
element: Element,
|
||||
{ width = Infinity, height = Infinity }: Partial<State> = {}
|
||||
): [React.ReactElement<any>, State] => {
|
||||
if (!isClient) {
|
||||
if (!isBrowser) {
|
||||
return [typeof element === 'function' ? element({ width, height }) : element, { width, height }];
|
||||
}
|
||||
|
||||
@ -43,7 +43,7 @@ const useSize = (
|
||||
setState(size);
|
||||
};
|
||||
const onWindow = (windowToListenOn: Window) => {
|
||||
windowToListenOn.addEventListener('resize', setSize);
|
||||
on(windowToListenOn, 'resize', setSize);
|
||||
DRAF(setSize);
|
||||
};
|
||||
|
||||
@ -61,17 +61,17 @@ const useSize = (
|
||||
onWindow(window);
|
||||
} else {
|
||||
const onLoad = () => {
|
||||
iframe.removeEventListener('load', onLoad);
|
||||
on(iframe, 'load', onLoad);
|
||||
window = iframe.contentWindow!;
|
||||
onWindow(window);
|
||||
};
|
||||
|
||||
iframe.addEventListener('load', onLoad);
|
||||
off(iframe, 'load', onLoad);
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (window && window.removeEventListener) {
|
||||
window.removeEventListener('resize', setSize);
|
||||
off(window, 'resize', setSize);
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import { useEffect, useRef, RefObject, CSSProperties } from 'react';
|
||||
|
||||
import { isClient, off, on } from './util';
|
||||
import { CSSProperties, RefObject, useEffect, useRef } from 'react';
|
||||
import { isBrowser, noop, off, on } from './misc/util';
|
||||
import useMountedState from './useMountedState';
|
||||
import useSetState from './useSetState';
|
||||
|
||||
@ -18,8 +17,6 @@ export interface Options {
|
||||
vertical?: boolean;
|
||||
}
|
||||
|
||||
const noop = () => {};
|
||||
|
||||
const useSlider = (ref: RefObject<HTMLElement>, options: Partial<Options> = {}): State => {
|
||||
const isMounted = useMountedState();
|
||||
const isSliding = useRef(false);
|
||||
@ -33,7 +30,7 @@ const useSlider = (ref: RefObject<HTMLElement>, options: Partial<Options> = {}):
|
||||
valueRef.current = state.value;
|
||||
|
||||
useEffect(() => {
|
||||
if (isClient) {
|
||||
if (isBrowser) {
|
||||
const styles = options.styles === undefined ? true : options.styles;
|
||||
const reverse = options.reverse === undefined ? false : options.reverse;
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { useRef } from 'react';
|
||||
import useMount from './useMount';
|
||||
import useSetState from './useSetState';
|
||||
import { isBrowser } from './misc/util';
|
||||
|
||||
export interface SpeechState {
|
||||
isPlaying: boolean;
|
||||
@ -19,8 +20,7 @@ export interface SpeechOptions {
|
||||
volume?: number;
|
||||
}
|
||||
|
||||
const voices =
|
||||
typeof window === 'object' && typeof window.speechSynthesis === 'object' ? window.speechSynthesis.getVoices() : [];
|
||||
const voices = isBrowser && typeof window.speechSynthesis === 'object' ? window.speechSynthesis.getVoices() : [];
|
||||
|
||||
const useSpeech = (text: string, opts: SpeechOptions = {}): SpeechState => {
|
||||
const [state, setState] = useSetState<SpeechState>({
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import useIsomorphicLayoutEffect from './useIsomorphicLayoutEffect';
|
||||
import { off, on } from './misc/util';
|
||||
|
||||
const isFocusedElementEditable = () => {
|
||||
const { activeElement, body } = document;
|
||||
@ -45,9 +46,9 @@ const useStartTyping = (onStartTyping: (event: KeyboardEvent) => void) => {
|
||||
!isFocusedElementEditable() && isTypedCharGood(event) && onStartTyping(event);
|
||||
};
|
||||
|
||||
document.addEventListener('keydown', keydown);
|
||||
on(document, 'keydown', keydown);
|
||||
return () => {
|
||||
document.removeEventListener('keydown', keydown);
|
||||
off(document, 'keydown', keydown);
|
||||
};
|
||||
}, []);
|
||||
};
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { Dispatch, useCallback, useMemo, useRef, useState } from 'react';
|
||||
import { useFirstMountState } from './useFirstMountState';
|
||||
import { InitialHookState, ResolvableHookState, resolveHookState } from './util/resolveHookState';
|
||||
import { IHookStateInitAction, IHookStateSetAction, resolveHookState } from './misc/hookState';
|
||||
|
||||
interface HistoryState<S> {
|
||||
history: S[];
|
||||
@ -11,17 +11,17 @@ interface HistoryState<S> {
|
||||
go: (position: number) => void;
|
||||
}
|
||||
|
||||
export type UseStateHistoryReturn<S> = [S, Dispatch<ResolvableHookState<S>>, HistoryState<S>];
|
||||
export type UseStateHistoryReturn<S> = [S, Dispatch<IHookStateSetAction<S>>, HistoryState<S>];
|
||||
|
||||
export function useStateWithHistory<S, I extends S>(
|
||||
initialState: InitialHookState<S>,
|
||||
initialState: IHookStateInitAction<S>,
|
||||
capacity?: number,
|
||||
initialHistory?: I[]
|
||||
): UseStateHistoryReturn<S>;
|
||||
export function useStateWithHistory<S = undefined>(): UseStateHistoryReturn<S | undefined>;
|
||||
|
||||
export function useStateWithHistory<S, I extends S>(
|
||||
initialState?: InitialHookState<S>,
|
||||
initialState?: IHookStateInitAction<S>,
|
||||
capacity: number = 10,
|
||||
initialHistory?: I[]
|
||||
): UseStateHistoryReturn<S> {
|
||||
@ -55,7 +55,7 @@ export function useStateWithHistory<S, I extends S>(
|
||||
}
|
||||
|
||||
const setState = useCallback(
|
||||
(newState: ResolvableHookState<S>): void => {
|
||||
(newState: IHookStateSetAction<S>): void => {
|
||||
innerSetState((currentState) => {
|
||||
newState = resolveHookState(newState);
|
||||
|
||||
@ -78,7 +78,7 @@ export function useStateWithHistory<S, I extends S>(
|
||||
});
|
||||
},
|
||||
[state, capacity]
|
||||
) as Dispatch<ResolvableHookState<S>>;
|
||||
) as Dispatch<IHookStateSetAction<S>>;
|
||||
|
||||
const historyState = useMemo(
|
||||
() => ({
|
||||
|
||||
@ -1,10 +1,13 @@
|
||||
import { useRef, useEffect } from 'react';
|
||||
import { useEffect, useRef } from 'react';
|
||||
|
||||
export interface UseTitleOptions {
|
||||
restoreOnUnmount?: boolean;
|
||||
}
|
||||
|
||||
const DEFAULT_USE_TITLE_OPTIONS: UseTitleOptions = {
|
||||
restoreOnUnmount: false,
|
||||
};
|
||||
|
||||
function useTitle(title: string, options: UseTitleOptions = DEFAULT_USE_TITLE_OPTIONS) {
|
||||
const prevTitleRef = useRef(document.title);
|
||||
document.title = title;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { useReducer, Reducer } from 'react';
|
||||
import { Reducer, useReducer } from 'react';
|
||||
|
||||
const toggleReducer = (state: boolean, nextValue?: any) => (typeof nextValue === 'boolean' ? nextValue : !state);
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { useMemo, useRef, useEffect } from 'react';
|
||||
import { useEffect, useMemo, useRef } from 'react';
|
||||
|
||||
export type Race = <P extends Promise<any>, E = any>(promise: P, onError?: (error: E) => void) => P;
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import useList, { ListActions } from './useList';
|
||||
import { InitialHookState } from './util/resolveHookState';
|
||||
import { IHookStateInitAction } from './misc/hookState';
|
||||
|
||||
export interface UpsertListActions<T> extends Omit<ListActions<T>, 'upsert'> {
|
||||
upsert: (newItem: T) => void;
|
||||
@ -10,7 +10,7 @@ export interface UpsertListActions<T> extends Omit<ListActions<T>, 'upsert'> {
|
||||
*/
|
||||
export default function useUpsert<T>(
|
||||
predicate: (a: T, b: T) => boolean,
|
||||
initialList: InitialHookState<T[]> = []
|
||||
initialList: IHookStateInitAction<T[]> = []
|
||||
): [T[], UpsertListActions<T>] {
|
||||
const [list, listActions] = useList(initialList);
|
||||
|
||||
|
||||
@ -1,11 +1,10 @@
|
||||
import { useEffect } from 'react';
|
||||
import { noop } from './misc/util';
|
||||
|
||||
export type VibrationPattern = number | number[];
|
||||
|
||||
const isVibrationApiSupported = typeof navigator === 'object' && 'vibrate' in navigator;
|
||||
|
||||
const useVibrateMock = () => {};
|
||||
|
||||
function useVibrate(enabled: boolean = true, pattern: VibrationPattern = [1000, 1000], loop: boolean = true): void {
|
||||
useEffect(() => {
|
||||
let interval;
|
||||
@ -34,4 +33,4 @@ function useVibrate(enabled: boolean = true, pattern: VibrationPattern = [1000,
|
||||
}, [enabled]);
|
||||
}
|
||||
|
||||
export default isVibrationApiSupported ? useVibrate : useVibrateMock;
|
||||
export default isVibrationApiSupported ? useVibrate : noop;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import createHTMLMediaHook from './util/createHTMLMediaHook';
|
||||
import createHTMLMediaHook from './factory/createHTMLMediaHook';
|
||||
|
||||
const useVideo = createHTMLMediaHook('video');
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { useEffect } from 'react';
|
||||
import { isClient } from './util';
|
||||
import { isBrowser, off, on } from './misc/util';
|
||||
|
||||
import useRafState from './useRafState';
|
||||
|
||||
@ -10,8 +10,8 @@ export interface State {
|
||||
|
||||
const useWindowScroll = (): State => {
|
||||
const [state, setState] = useRafState<State>({
|
||||
x: isClient ? window.pageXOffset : 0,
|
||||
y: isClient ? window.pageYOffset : 0,
|
||||
x: isBrowser ? window.pageXOffset : 0,
|
||||
y: isBrowser ? window.pageYOffset : 0,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
@ -22,13 +22,13 @@ const useWindowScroll = (): State => {
|
||||
});
|
||||
};
|
||||
|
||||
window.addEventListener('scroll', handler, {
|
||||
on(window, 'scroll', handler, {
|
||||
capture: false,
|
||||
passive: true,
|
||||
});
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('scroll', handler);
|
||||
off(window, 'scroll', handler);
|
||||
};
|
||||
}, []);
|
||||
|
||||
|
||||
@ -1,16 +1,16 @@
|
||||
import { useEffect } from 'react';
|
||||
|
||||
import useRafState from './useRafState';
|
||||
import { isClient } from './util';
|
||||
import { isBrowser, off, on } from './misc/util';
|
||||
|
||||
const useWindowSize = (initialWidth = Infinity, initialHeight = Infinity) => {
|
||||
const [state, setState] = useRafState<{ width: number; height: number }>({
|
||||
width: isClient ? window.innerWidth : initialWidth,
|
||||
height: isClient ? window.innerHeight : initialHeight,
|
||||
width: isBrowser ? window.innerWidth : initialWidth,
|
||||
height: isBrowser ? window.innerHeight : initialHeight,
|
||||
});
|
||||
|
||||
useEffect((): (() => void) | void => {
|
||||
if (isClient) {
|
||||
if (isBrowser) {
|
||||
const handler = () => {
|
||||
setState({
|
||||
width: window.innerWidth,
|
||||
@ -18,10 +18,10 @@ const useWindowSize = (initialWidth = Infinity, initialHeight = Infinity) => {
|
||||
});
|
||||
};
|
||||
|
||||
window.addEventListener('resize', handler);
|
||||
on(window, 'resize', handler);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('resize', handler);
|
||||
off(window, 'resize', handler);
|
||||
};
|
||||
}
|
||||
}, []);
|
||||
|
||||
13
src/util.ts
13
src/util.ts
@ -1,13 +0,0 @@
|
||||
import isDeepEqualReact from 'fast-deep-equal/react';
|
||||
|
||||
export const isClient = typeof window === 'object';
|
||||
|
||||
export const on = (obj: any, ...args: any[]) => obj.addEventListener(...args);
|
||||
|
||||
export const off = (obj: any, ...args: any[]) => obj.removeEventListener(...args);
|
||||
|
||||
export type FnReturningPromise = (...args: any[]) => Promise<any>;
|
||||
|
||||
export type PromiseType<P extends Promise<any>> = P extends Promise<infer T> ? T : never;
|
||||
|
||||
export const isDeepEqual: (a: any, b: any) => boolean = isDeepEqualReact;
|
||||
@ -1,237 +0,0 @@
|
||||
import * as React from 'react';
|
||||
import { useEffect, useRef } from 'react';
|
||||
import useSetState from '../useSetState';
|
||||
import parseTimeRanges from './parseTimeRanges';
|
||||
|
||||
export interface HTMLMediaProps extends React.AudioHTMLAttributes<any>, React.VideoHTMLAttributes<any> {
|
||||
src: string;
|
||||
}
|
||||
|
||||
export interface HTMLMediaState {
|
||||
buffered: any[];
|
||||
duration: number;
|
||||
paused: boolean;
|
||||
muted: boolean;
|
||||
time: number;
|
||||
volume: number;
|
||||
}
|
||||
|
||||
export interface HTMLMediaControls {
|
||||
play: () => Promise<void> | void;
|
||||
pause: () => void;
|
||||
mute: () => void;
|
||||
unmute: () => void;
|
||||
volume: (volume: number) => void;
|
||||
seek: (time: number) => void;
|
||||
}
|
||||
|
||||
type createHTMLMediaHookReturn = [
|
||||
React.ReactElement<HTMLMediaProps>,
|
||||
HTMLMediaState,
|
||||
HTMLMediaControls,
|
||||
{ current: HTMLAudioElement | null }
|
||||
];
|
||||
|
||||
const createHTMLMediaHook = (tag: 'audio' | 'video') => (
|
||||
elOrProps: HTMLMediaProps | React.ReactElement<HTMLMediaProps>
|
||||
): createHTMLMediaHookReturn => {
|
||||
let element: React.ReactElement<any> | undefined;
|
||||
let props: HTMLMediaProps;
|
||||
|
||||
if (React.isValidElement(elOrProps)) {
|
||||
element = elOrProps;
|
||||
props = element.props;
|
||||
} else {
|
||||
props = elOrProps as HTMLMediaProps;
|
||||
}
|
||||
|
||||
const [state, setState] = useSetState<HTMLMediaState>({
|
||||
buffered: [],
|
||||
time: 0,
|
||||
duration: 0,
|
||||
paused: true,
|
||||
muted: false,
|
||||
volume: 1,
|
||||
});
|
||||
const ref = useRef<HTMLAudioElement | null>(null);
|
||||
|
||||
const wrapEvent = (userEvent, proxyEvent?) => {
|
||||
return (event) => {
|
||||
try {
|
||||
proxyEvent && proxyEvent(event);
|
||||
} finally {
|
||||
userEvent && userEvent(event);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const onPlay = () => setState({ paused: false });
|
||||
const onPause = () => setState({ paused: true });
|
||||
const onVolumeChange = () => {
|
||||
const el = ref.current;
|
||||
if (!el) {
|
||||
return;
|
||||
}
|
||||
setState({
|
||||
muted: el.muted,
|
||||
volume: el.volume,
|
||||
});
|
||||
};
|
||||
const onDurationChange = () => {
|
||||
const el = ref.current;
|
||||
if (!el) {
|
||||
return;
|
||||
}
|
||||
const { duration, buffered } = el;
|
||||
setState({
|
||||
duration,
|
||||
buffered: parseTimeRanges(buffered),
|
||||
});
|
||||
};
|
||||
const onTimeUpdate = () => {
|
||||
const el = ref.current;
|
||||
if (!el) {
|
||||
return;
|
||||
}
|
||||
setState({ time: el.currentTime });
|
||||
};
|
||||
const onProgress = () => {
|
||||
const el = ref.current;
|
||||
if (!el) {
|
||||
return;
|
||||
}
|
||||
setState({ buffered: parseTimeRanges(el.buffered) });
|
||||
};
|
||||
|
||||
if (element) {
|
||||
element = React.cloneElement(element, {
|
||||
controls: false,
|
||||
...props,
|
||||
ref,
|
||||
onPlay: wrapEvent(props.onPlay, onPlay),
|
||||
onPause: wrapEvent(props.onPause, onPause),
|
||||
onVolumeChange: wrapEvent(props.onVolumeChange, onVolumeChange),
|
||||
onDurationChange: wrapEvent(props.onDurationChange, onDurationChange),
|
||||
onTimeUpdate: wrapEvent(props.onTimeUpdate, onTimeUpdate),
|
||||
onProgress: wrapEvent(props.onProgress, onProgress),
|
||||
});
|
||||
} else {
|
||||
element = React.createElement(tag, {
|
||||
controls: false,
|
||||
...props,
|
||||
ref,
|
||||
onPlay: wrapEvent(props.onPlay, onPlay),
|
||||
onPause: wrapEvent(props.onPause, onPause),
|
||||
onVolumeChange: wrapEvent(props.onVolumeChange, onVolumeChange),
|
||||
onDurationChange: wrapEvent(props.onDurationChange, onDurationChange),
|
||||
onTimeUpdate: wrapEvent(props.onTimeUpdate, onTimeUpdate),
|
||||
onProgress: wrapEvent(props.onProgress, onProgress),
|
||||
} as any); // TODO: fix this typing.
|
||||
}
|
||||
|
||||
// Some browsers return `Promise` on `.play()` and may throw errors
|
||||
// if one tries to execute another `.play()` or `.pause()` while that
|
||||
// promise is resolving. So we prevent that with this lock.
|
||||
// See: https://bugs.chromium.org/p/chromium/issues/detail?id=593273
|
||||
let lockPlay: boolean = false;
|
||||
|
||||
const controls = {
|
||||
play: () => {
|
||||
const el = ref.current;
|
||||
if (!el) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (!lockPlay) {
|
||||
const promise = el.play();
|
||||
const isPromise = typeof promise === 'object';
|
||||
|
||||
if (isPromise) {
|
||||
lockPlay = true;
|
||||
const resetLock = () => {
|
||||
lockPlay = false;
|
||||
};
|
||||
promise.then(resetLock, resetLock);
|
||||
}
|
||||
|
||||
return promise;
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
pause: () => {
|
||||
const el = ref.current;
|
||||
if (el && !lockPlay) {
|
||||
return el.pause();
|
||||
}
|
||||
},
|
||||
seek: (time: number) => {
|
||||
const el = ref.current;
|
||||
if (!el || state.duration === undefined) {
|
||||
return;
|
||||
}
|
||||
time = Math.min(state.duration, Math.max(0, time));
|
||||
el.currentTime = time;
|
||||
},
|
||||
volume: (volume: number) => {
|
||||
const el = ref.current;
|
||||
if (!el) {
|
||||
return;
|
||||
}
|
||||
volume = Math.min(1, Math.max(0, volume));
|
||||
el.volume = volume;
|
||||
setState({ volume });
|
||||
},
|
||||
mute: () => {
|
||||
const el = ref.current;
|
||||
if (!el) {
|
||||
return;
|
||||
}
|
||||
el.muted = true;
|
||||
},
|
||||
unmute: () => {
|
||||
const el = ref.current;
|
||||
if (!el) {
|
||||
return;
|
||||
}
|
||||
el.muted = false;
|
||||
},
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const el = ref.current!;
|
||||
|
||||
if (!el) {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
if (tag === 'audio') {
|
||||
console.error(
|
||||
'useAudio() ref to <audio> element is empty at mount. ' +
|
||||
'It seem you have not rendered the audio element, which it ' +
|
||||
'returns as the first argument const [audio] = useAudio(...).'
|
||||
);
|
||||
} else if (tag === 'video') {
|
||||
console.error(
|
||||
'useVideo() ref to <video> element is empty at mount. ' +
|
||||
'It seem you have not rendered the video element, which it ' +
|
||||
'returns as the first argument const [video] = useVideo(...).'
|
||||
);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
setState({
|
||||
volume: el.volume,
|
||||
muted: el.muted,
|
||||
paused: el.paused,
|
||||
});
|
||||
|
||||
// Start media, if autoPlay requested.
|
||||
if (props.autoPlay && el.paused) {
|
||||
controls.play();
|
||||
}
|
||||
}, [props.src]);
|
||||
|
||||
return [element, state, controls, ref];
|
||||
};
|
||||
|
||||
export default createHTMLMediaHook;
|
||||
@ -1,17 +0,0 @@
|
||||
export type StateSetter<S> = (prevState: S) => S;
|
||||
export type InitialStateSetter<S> = () => S;
|
||||
|
||||
export type InitialHookState<S> = S | InitialStateSetter<S>;
|
||||
export type HookState<S> = S | StateSetter<S>;
|
||||
export type ResolvableHookState<S> = S | StateSetter<S> | InitialStateSetter<S>;
|
||||
|
||||
export function resolveHookState<S, C extends S>(newState: InitialStateSetter<S>): S;
|
||||
export function resolveHookState<S, C extends S>(newState: StateSetter<S>, currentState: C): S;
|
||||
export function resolveHookState<S, C extends S>(newState: ResolvableHookState<S>, currentState?: C): S;
|
||||
export function resolveHookState<S, C extends S>(newState: ResolvableHookState<S>, currentState?: C): S {
|
||||
if (typeof newState === 'function') {
|
||||
return (newState as Function)(currentState);
|
||||
}
|
||||
|
||||
return newState;
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
import { storiesOf } from '@storybook/react';
|
||||
import * as React from 'react';
|
||||
import UseKey from '../../src/comps/UseKey';
|
||||
import UseKey from '../../src/component/UseKey';
|
||||
|
||||
storiesOf('Components/<UseKey>', module).add('Demo', () => (
|
||||
<div>
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import { storiesOf } from '@storybook/react';
|
||||
import * as React from 'react';
|
||||
import { useCounter, useCustomCompareEffect } from '../src';
|
||||
import { isDeepEqual } from '../src/util';
|
||||
import ShowDocs from './util/ShowDocs';
|
||||
import isDeepEqual from '../src/misc/isDeepEqual';
|
||||
|
||||
const Demo = () => {
|
||||
const [countNormal, { inc: incNormal }] = useCounter(0);
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { act, renderHook } from '@testing-library/react-hooks';
|
||||
import createBreakpoint from '../src/createBreakpoint';
|
||||
import createBreakpoint from '../src/factory/createBreakpoint';
|
||||
|
||||
const useBreakpointA = createBreakpoint();
|
||||
const useBreakpointB = createBreakpoint({ mobileM: 350, laptop: 1024, tablet: 768 });
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { act, renderHook } from '@testing-library/react-hooks';
|
||||
import createGlobalState from '../src/createGlobalState';
|
||||
import createGlobalState from '../src/factory/createGlobalState';
|
||||
|
||||
describe('useGlobalState', () => {
|
||||
it('should be defined', () => {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { renderHook } from '@testing-library/react-hooks';
|
||||
import createMemo from '../src/createMemo';
|
||||
import createMemo from '../src/factory/createMemo';
|
||||
|
||||
const getDouble = jest.fn((n: number): number => n * 2);
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { act, renderHook } from '@testing-library/react-hooks';
|
||||
import logger from 'redux-logger';
|
||||
import thunk from 'redux-thunk';
|
||||
import createReducer from '../src/createReducer';
|
||||
import createReducer from '../src/factory/createReducer';
|
||||
|
||||
it('should init reducer hook function', () => {
|
||||
const useSomeReducer = createReducer();
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import { render, fireEvent } from '@testing-library/react';
|
||||
import { act, renderHook } from '@testing-library/react-hooks';
|
||||
import createReducerContext from '../src/createReducerContext';
|
||||
import createReducerContext from '../src/factory/createReducerContext';
|
||||
|
||||
type Action = 'increment' | 'decrement';
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import { render, fireEvent } from '@testing-library/react';
|
||||
import { act, renderHook } from '@testing-library/react-hooks';
|
||||
import createStateContext from '../src/createStateContext';
|
||||
import createStateContext from '../src/factory/createStateContext';
|
||||
|
||||
it('should create a hook and a provider', () => {
|
||||
const [useSharedNumber, SharedNumberProvider] = createStateContext(0);
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { resolveHookState } from '../src/util/resolveHookState';
|
||||
import { resolveHookState } from '../../src/misc/hookState';
|
||||
|
||||
describe('resolveHookState', () => {
|
||||
it('should defined', () => {
|
||||
@ -17,10 +17,19 @@ describe('resolveHookState', () => {
|
||||
expect(spy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should pass 2nd parameter to function', () => {
|
||||
const spy = jest.fn();
|
||||
it('should pass 2nd parameter to function if it awaited', () => {
|
||||
const spy = jest.fn((n: number) => n);
|
||||
resolveHookState(spy, 123);
|
||||
expect(spy).toHaveBeenCalled();
|
||||
expect(spy.mock.calls[0][0]).toBe(123);
|
||||
});
|
||||
|
||||
it('should not pass 2nd parameter to function if it not awaited', () => {
|
||||
const spy = jest.fn(() => {
|
||||
});
|
||||
/* @ts-expect-error */
|
||||
resolveHookState(spy, 123);
|
||||
expect(spy).toHaveBeenCalled();
|
||||
expect(spy.mock.calls[0].length).toBe(0);
|
||||
});
|
||||
});
|
||||
@ -1,9 +1,8 @@
|
||||
import writeText from 'copy-to-clipboard';
|
||||
import { renderHook, act } from '@testing-library/react-hooks';
|
||||
import { act, renderHook } from '@testing-library/react-hooks';
|
||||
import { useCopyToClipboard } from '../src';
|
||||
|
||||
const valueToRaiseMockException = 'fake input causing exception in copy to clipboard';
|
||||
|
||||
jest.mock('copy-to-clipboard', () =>
|
||||
jest.fn().mockImplementation(input => {
|
||||
if (input === valueToRaiseMockException) {
|
||||
@ -12,17 +11,21 @@ jest.mock('copy-to-clipboard', () =>
|
||||
return true;
|
||||
})
|
||||
);
|
||||
jest.spyOn(global.console, 'error').mockImplementation(() => {});
|
||||
|
||||
describe('useCopyToClipboard', () => {
|
||||
let hook;
|
||||
let consoleErrorSpy = jest.spyOn(global.console, 'error').mockImplementation(() => {
|
||||
});
|
||||
|
||||
|
||||
beforeEach(() => {
|
||||
hook = renderHook(() => useCopyToClipboard());
|
||||
});
|
||||
|
||||
|
||||
afterAll(() => {
|
||||
jest.restoreAllMocks();
|
||||
consoleErrorSpy.mockRestore();
|
||||
jest.unmock('copy-to-clipboard');
|
||||
});
|
||||
|
||||
it('should be defined ', () => {
|
||||
@ -95,7 +98,7 @@ describe('useCopyToClipboard', () => {
|
||||
[state, copyToClipboard] = hook.result.current;
|
||||
|
||||
expect(writeText).not.toBeCalled();
|
||||
expect(console.error).toBeCalled();
|
||||
expect(consoleErrorSpy).toBeCalled();
|
||||
expect(state.value).toBe(testValue);
|
||||
expect(state.noUserInteraction).toBe(true);
|
||||
expect(state.error).toBeDefined();
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { renderHook } from '@testing-library/react-hooks';
|
||||
import { useCustomCompareEffect } from '../src';
|
||||
import { useEffect } from 'react';
|
||||
import { isDeepEqual } from '../src/util';
|
||||
import isDeepEqual from '../src/misc/isDeepEqual';
|
||||
|
||||
let options = { max: 10 };
|
||||
const mockEffectNormal = jest.fn();
|
||||
|
||||
@ -30,7 +30,7 @@ it('synchronously sets up ResizeObserver listener', () => {
|
||||
};
|
||||
|
||||
const { result } = renderHook(() => useMeasure());
|
||||
|
||||
|
||||
act(() => {
|
||||
const div = document.createElement('div');
|
||||
(result.current[0] as UseMeasureRef)(div);
|
||||
@ -50,12 +50,12 @@ it('tracks rectangle of a DOM element', () => {
|
||||
};
|
||||
|
||||
const { result } = renderHook(() => useMeasure());
|
||||
|
||||
|
||||
act(() => {
|
||||
const div = document.createElement('div');
|
||||
(result.current[0] as UseMeasureRef)(div);
|
||||
});
|
||||
|
||||
|
||||
act(() => {
|
||||
listener!([{
|
||||
contentRect: {
|
||||
@ -94,12 +94,12 @@ it('tracks multiple updates', () => {
|
||||
};
|
||||
|
||||
const { result } = renderHook(() => useMeasure());
|
||||
|
||||
|
||||
act(() => {
|
||||
const div = document.createElement('div');
|
||||
(result.current[0] as UseMeasureRef)(div);
|
||||
});
|
||||
|
||||
|
||||
act(() => {
|
||||
listener!([{
|
||||
contentRect: {
|
||||
@ -125,7 +125,7 @@ it('tracks multiple updates', () => {
|
||||
left: 1,
|
||||
right: 1,
|
||||
});
|
||||
|
||||
|
||||
act(() => {
|
||||
listener!([{
|
||||
contentRect: {
|
||||
@ -163,7 +163,7 @@ it('calls .disconnect() on ResizeObserver when component unmounts', () => {
|
||||
};
|
||||
|
||||
const { result, unmount } = renderHook(() => useMeasure());
|
||||
|
||||
|
||||
act(() => {
|
||||
const div = document.createElement('div');
|
||||
(result.current[0] as UseMeasureRef)(div);
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { act, renderHook, RenderHookResult } from '@testing-library/react-hooks';
|
||||
import { useRef } from 'react';
|
||||
import { UseStateHistoryReturn, useStateWithHistory } from '../src/useStateWithHistory';
|
||||
import { InitialHookState } from '../src/util/resolveHookState';
|
||||
import { IHookStateSetAction } from '../src/misc/hookState';
|
||||
|
||||
describe('useStateWithHistory', () => {
|
||||
it('should be defined', () => {
|
||||
@ -9,7 +9,7 @@ describe('useStateWithHistory', () => {
|
||||
});
|
||||
|
||||
function getHook<S, I extends S>(
|
||||
initialState?: InitialHookState<S>,
|
||||
initialState?: IHookStateSetAction<S>,
|
||||
initialCapacity?: number,
|
||||
initialHistory?: I[]
|
||||
): RenderHookResult<{ state?: S; history?: I[]; capacity?: number }, [UseStateHistoryReturn<S | undefined>, number]> {
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { act, renderHook } from '@testing-library/react-hooks';
|
||||
import { replaceRaf } from 'raf-stub';
|
||||
import useWindowSize from '../src/useWindowSize';
|
||||
import { isClient } from '../src/util';
|
||||
import { isBrowser } from '../src/misc/util';
|
||||
|
||||
declare var requestAnimationFrame: {
|
||||
reset: () => void;
|
||||
@ -46,8 +46,8 @@ describe('useWindowSize', () => {
|
||||
it('should use passed parameters as initial values in case of non-browser use', () => {
|
||||
const hook = getHook(1, 1);
|
||||
|
||||
expect(hook.result.current.height).toBe(isClient ? window.innerHeight : 1);
|
||||
expect(hook.result.current.width).toBe(isClient ? window.innerWidth : 1);
|
||||
expect(hook.result.current.height).toBe(isBrowser ? window.innerHeight : 1);
|
||||
expect(hook.result.current.width).toBe(isBrowser ? window.innerWidth : 1);
|
||||
});
|
||||
|
||||
it('should re-render after height change on closest RAF', () => {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user