mirror of
https://github.com/streamich/react-use.git
synced 2026-01-18 14:06:52 +00:00
feat(prettier): make prettier a part of eslint.
Also reduce max line width to 100. And remove `lint:types` step for commit sequence, it bothers when committing incomplete (wip) changes.
This commit is contained in:
parent
f938995c80
commit
e6fae504b8
@ -8,7 +8,7 @@ indent_size = 2
|
||||
indent_style = space
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
max_line_length = 120
|
||||
max_line_length = 100
|
||||
|
||||
[*.{ts, tsx}]
|
||||
ij_typescript_enforce_trailing_comma = keep
|
||||
@ -24,7 +24,7 @@ ij_typescript_catch_on_new_line = false
|
||||
ij_typescript_spaces_within_interpolation_expressions = false
|
||||
|
||||
[*.md]
|
||||
max_line_length = 120
|
||||
max_line_length = 100
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[COMMIT_EDITMSG]
|
||||
|
||||
22
.eslintrc.js
Normal file
22
.eslintrc.js
Normal file
@ -0,0 +1,22 @@
|
||||
module.exports = {
|
||||
extends: ['prettier/@typescript-eslint', 'react-app', 'plugin:prettier/recommended'],
|
||||
plugins: ['prettier'],
|
||||
rules: {
|
||||
'prettier/prettier': [
|
||||
'error',
|
||||
{
|
||||
singleQuote: true,
|
||||
trailingComma: 'es5',
|
||||
tabWidth: 2,
|
||||
printWidth: 100,
|
||||
semicolons: true,
|
||||
quoteProps: 'as-needed',
|
||||
jsxSingleQuote: false,
|
||||
bracketSpacing: true,
|
||||
jsxBracketSameLine: true,
|
||||
arrowParens: 'always',
|
||||
endOfLine: 'lf',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
@ -1,3 +0,0 @@
|
||||
{
|
||||
"extends": "react-app"
|
||||
}
|
||||
@ -1,6 +0,0 @@
|
||||
{
|
||||
"trailingComma": "es5",
|
||||
"singleQuote": true,
|
||||
"printWidth": 120,
|
||||
"tabWidth": 2
|
||||
}
|
||||
10
package.json
10
package.json
@ -16,10 +16,9 @@
|
||||
"test": "jest --maxWorkers 2",
|
||||
"test:watch": "jest --watch",
|
||||
"test:coverage": "jest --coverage",
|
||||
"lint": "eslint {src,tests}/**/*.{ts,tsx}",
|
||||
"lint": "eslint {src,tests,stories}/**/*.{ts,tsx}",
|
||||
"lint:fix": "yarn lint --fix",
|
||||
"lint:types": "tsc --noEmit",
|
||||
"lint:prettier": "prettier --write src/**/**/*.{ts,tsx}",
|
||||
"build:cjs": "tsc",
|
||||
"build:es": "tsc -m esNext --outDir esm",
|
||||
"build": "yarn build:cjs && yarn build:es",
|
||||
@ -32,7 +31,7 @@
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-commit": "yarn lint:types && lint-staged",
|
||||
"pre-commit": "lint-staged",
|
||||
"pre-push": "yarn lint && yarn clean && yarn build && yarn test"
|
||||
}
|
||||
},
|
||||
@ -92,10 +91,12 @@
|
||||
"babel-loader": "8.2.2",
|
||||
"babel-plugin-dynamic-import-node": "2.3.3",
|
||||
"eslint": "7.18.0",
|
||||
"eslint-config-prettier": "^7.2.0",
|
||||
"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-prettier": "^3.3.1",
|
||||
"eslint-plugin-react": "7.22.0",
|
||||
"eslint-plugin-react-hooks": "4.2.0",
|
||||
"fork-ts-checker-webpack-plugin": "6.1.0",
|
||||
@ -106,7 +107,7 @@
|
||||
"keyboardjs": "2.6.4",
|
||||
"lint-staged": "10.5.3",
|
||||
"markdown-loader": "6.0.0",
|
||||
"prettier": "2.2.1",
|
||||
"prettier": "^2.2.1",
|
||||
"raf-stub": "3.0.0",
|
||||
"react": "17.0.1",
|
||||
"react-dom": "17.0.1",
|
||||
@ -151,7 +152,6 @@
|
||||
"lint-staged": {
|
||||
"src/**/**/*.{ts,tsx}": [
|
||||
"eslint --fix",
|
||||
"prettier --write",
|
||||
"git add"
|
||||
]
|
||||
},
|
||||
|
||||
@ -1,6 +1,11 @@
|
||||
import useKey from '../useKey';
|
||||
import createRenderProp from '../factory/createRenderProp';
|
||||
|
||||
const UseKey = createRenderProp(useKey, ({ filter, fn, deps, ...rest }) => [filter, fn, rest, deps]);
|
||||
const UseKey = createRenderProp(useKey, ({ filter, fn, deps, ...rest }) => [
|
||||
filter,
|
||||
fn,
|
||||
rest,
|
||||
deps,
|
||||
]);
|
||||
|
||||
export default UseKey;
|
||||
|
||||
@ -16,9 +16,10 @@ const createBreakpoint = (
|
||||
off(window, 'resize', setSideScreen);
|
||||
};
|
||||
});
|
||||
const sortedBreakpoints = useMemo(() => Object.entries(breakpoints).sort((a, b) => (a[1] >= b[1] ? 1 : -1)), [
|
||||
breakpoints,
|
||||
]);
|
||||
const sortedBreakpoints = useMemo(
|
||||
() => Object.entries(breakpoints).sort((a, b) => (a[1] >= b[1] ? 1 : -1)),
|
||||
[breakpoints]
|
||||
);
|
||||
const result = sortedBreakpoints.reduce((acc, [name, width]) => {
|
||||
if (screen >= width) {
|
||||
return name;
|
||||
|
||||
@ -3,7 +3,9 @@ import { useEffect, useRef } from 'react';
|
||||
import useSetState from '../useSetState';
|
||||
import parseTimeRanges from '../misc/parseTimeRanges';
|
||||
|
||||
export interface HTMLMediaProps extends React.AudioHTMLAttributes<any>, React.VideoHTMLAttributes<any> {
|
||||
export interface HTMLMediaProps
|
||||
extends React.AudioHTMLAttributes<any>,
|
||||
React.VideoHTMLAttributes<any> {
|
||||
src: string;
|
||||
}
|
||||
|
||||
@ -33,7 +35,9 @@ type createHTMLMediaHookReturn = [
|
||||
];
|
||||
|
||||
export default function createHTMLMediaHook(tag: 'audio' | 'video') {
|
||||
return (elOrProps: HTMLMediaProps | React.ReactElement<HTMLMediaProps>): createHTMLMediaHookReturn => {
|
||||
return (
|
||||
elOrProps: HTMLMediaProps | React.ReactElement<HTMLMediaProps>
|
||||
): createHTMLMediaHookReturn => {
|
||||
let element: React.ReactElement<any> | undefined;
|
||||
let props: HTMLMediaProps;
|
||||
|
||||
|
||||
@ -8,7 +8,9 @@ interface Store<Action, State> {
|
||||
dispatch: Dispatch<Action>;
|
||||
}
|
||||
|
||||
type Middleware<Action, State> = (store: Store<Action, State>) => (next: Dispatch<Action>) => (action: Action) => void;
|
||||
type Middleware<Action, State> = (
|
||||
store: Store<Action, State>
|
||||
) => (next: Dispatch<Action>) => (action: Action) => void;
|
||||
|
||||
function composeMiddleware<Action, State>(chain: Middleware<Action, State>[]) {
|
||||
return (context: Store<Action, State>, dispatch: Dispatch<Action>) => {
|
||||
|
||||
@ -4,11 +4,19 @@ const createReducerContext = <R extends React.Reducer<any, any>>(
|
||||
reducer: R,
|
||||
defaultInitialState: React.ReducerState<R>
|
||||
) => {
|
||||
const context = createContext<[React.ReducerState<R>, React.Dispatch<React.ReducerAction<R>>] | undefined>(undefined);
|
||||
const context = createContext<
|
||||
[React.ReducerState<R>, React.Dispatch<React.ReducerAction<R>>] | undefined
|
||||
>(undefined);
|
||||
const providerFactory = (props, children) => createElement(context.Provider, props, children);
|
||||
|
||||
const ReducerProvider: React.FC<{ initialState?: React.ReducerState<R> }> = ({ children, initialState }) => {
|
||||
const state = useReducer<R>(reducer, initialState !== undefined ? initialState : defaultInitialState);
|
||||
const ReducerProvider: React.FC<{ initialState?: React.ReducerState<R> }> = ({
|
||||
children,
|
||||
initialState,
|
||||
}) => {
|
||||
const state = useReducer<R>(
|
||||
reducer,
|
||||
initialState !== undefined ? initialState : defaultInitialState
|
||||
);
|
||||
return providerFactory({ value: state }, children);
|
||||
};
|
||||
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
import { createContext, createElement, useContext, useState } from 'react';
|
||||
|
||||
const createStateContext = <T>(defaultInitialValue: T) => {
|
||||
const context = createContext<[T, React.Dispatch<React.SetStateAction<T>>] | undefined>(undefined);
|
||||
const context = createContext<[T, React.Dispatch<React.SetStateAction<T>>] | undefined>(
|
||||
undefined
|
||||
);
|
||||
const providerFactory = (props, children) => createElement(context.Provider, props, children);
|
||||
|
||||
const StateProvider: React.FC<{ initialValue?: T }> = ({ children, initialValue }) => {
|
||||
|
||||
@ -7,9 +7,18 @@ 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 {
|
||||
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)();
|
||||
}
|
||||
|
||||
@ -4,7 +4,10 @@ import { FunctionReturningPromise } from './misc/types';
|
||||
|
||||
export { AsyncState, AsyncFnReturn } from './useAsyncFn';
|
||||
|
||||
export default function useAsync<T extends FunctionReturningPromise>(fn: T, deps: DependencyList = []) {
|
||||
export default function useAsync<T extends FunctionReturningPromise>(
|
||||
fn: T,
|
||||
deps: DependencyList = []
|
||||
) {
|
||||
const [state, callback] = useAsyncFn(fn, deps, {
|
||||
loading: true,
|
||||
});
|
||||
|
||||
@ -24,7 +24,9 @@ export type AsyncState<T> =
|
||||
value: T;
|
||||
};
|
||||
|
||||
type StateFromFunctionReturningPromise<T extends FunctionReturningPromise> = AsyncState<PromiseType<ReturnType<T>>>;
|
||||
type StateFromFunctionReturningPromise<T extends FunctionReturningPromise> = AsyncState<
|
||||
PromiseType<ReturnType<T>>
|
||||
>;
|
||||
|
||||
export type AsyncFnReturn<T extends FunctionReturningPromise = FunctionReturningPromise> = [
|
||||
StateFromFunctionReturningPromise<T>,
|
||||
|
||||
@ -13,7 +13,9 @@ const useAsyncRetry = <T>(fn: () => Promise<T>, deps: DependencyList = []) => {
|
||||
const retry = useCallback(() => {
|
||||
if (stateLoading) {
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
console.log('You are calling useAsyncRetry hook retry() method while loading in progress, this is a no-op.');
|
||||
console.log(
|
||||
'You are calling useAsyncRetry hook retry() method while loading in progress, this is a no-op.'
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
@ -25,7 +25,8 @@ type UseBatteryState =
|
||||
| { isSupported: true; fetched: false } // battery API supported but not fetched yet
|
||||
| (BatteryState & { isSupported: true; fetched: true }); // battery API supported and fetched
|
||||
|
||||
const nav: NavigatorWithPossibleBattery | undefined = typeof navigator === 'object' ? navigator : undefined;
|
||||
const nav: NavigatorWithPossibleBattery | undefined =
|
||||
typeof navigator === 'object' ? navigator : undefined;
|
||||
const isBatteryApiSupported = nav && typeof nav.getBattery === 'function';
|
||||
|
||||
function useBatteryMock(): UseBatteryState {
|
||||
|
||||
@ -26,7 +26,9 @@ const useCopyToClipboard = (): [CopyToClipboardState, (value: string) => void] =
|
||||
try {
|
||||
// only strings and numbers casted to strings can be copied to clipboard
|
||||
if (typeof value !== 'string' && typeof value !== 'number') {
|
||||
const error = new Error(`Cannot copy typeof ${typeof value} to clipboard, must be a string`);
|
||||
const error = new Error(
|
||||
`Cannot copy typeof ${typeof value} to clipboard, must be a string`
|
||||
);
|
||||
if (process.env.NODE_ENV === 'development') console.error(error);
|
||||
setState({
|
||||
value,
|
||||
|
||||
@ -17,7 +17,8 @@ export default function useCounter(
|
||||
): [number, CounterActions] {
|
||||
let init = resolveHookState(initialValue);
|
||||
|
||||
typeof init !== 'number' && console.error('initialValue has to be a number, got ' + typeof initialValue);
|
||||
typeof init !== 'number' &&
|
||||
console.error('initialValue has to be a number, got ' + typeof initialValue);
|
||||
|
||||
if (typeof min === 'number') {
|
||||
init = Math.max(init, min);
|
||||
@ -59,7 +60,9 @@ export default function useCounter(
|
||||
const rDelta = resolveHookState(delta, get());
|
||||
|
||||
if (typeof rDelta !== 'number') {
|
||||
console.error('delta has to be a number or function returning a number, got ' + typeof rDelta);
|
||||
console.error(
|
||||
'delta has to be a number or function returning a number, got ' + typeof rDelta
|
||||
);
|
||||
}
|
||||
|
||||
set((num: number) => num + rDelta);
|
||||
@ -68,7 +71,9 @@ export default function useCounter(
|
||||
const rDelta = resolveHookState(delta, get());
|
||||
|
||||
if (typeof rDelta !== 'number') {
|
||||
console.error('delta has to be a number or function returning a number, got ' + typeof rDelta);
|
||||
console.error(
|
||||
'delta has to be a number or function returning a number, got ' + typeof rDelta
|
||||
);
|
||||
}
|
||||
|
||||
set((num: number) => num - rDelta);
|
||||
@ -77,7 +82,9 @@ export default function useCounter(
|
||||
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);
|
||||
console.error(
|
||||
'value has to be a number or function returning a number, got ' + typeof rValue
|
||||
);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
|
||||
@ -11,7 +11,9 @@ const useCustomCompareEffect = <TDeps extends DependencyList>(
|
||||
) => {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
if (!(deps instanceof Array) || !deps.length) {
|
||||
console.warn('`useCustomCompareEffect` should not be used with no dependencies. Use React.useEffect instead.');
|
||||
console.warn(
|
||||
'`useCustomCompareEffect` should not be used with no dependencies. Use React.useEffect instead.'
|
||||
);
|
||||
}
|
||||
|
||||
if (deps.every(isPrimitive)) {
|
||||
@ -21,7 +23,9 @@ const useCustomCompareEffect = <TDeps extends DependencyList>(
|
||||
}
|
||||
|
||||
if (typeof depsEqual !== 'function') {
|
||||
console.warn('`useCustomCompareEffect` should be used with depsEqual callback for comparing deps list');
|
||||
console.warn(
|
||||
'`useCustomCompareEffect` should be used with depsEqual callback for comparing deps list'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -3,7 +3,11 @@ import useTimeoutFn from './useTimeoutFn';
|
||||
|
||||
export type UseDebounceReturn = [() => boolean | null, () => void];
|
||||
|
||||
export default function useDebounce(fn: Function, ms: number = 0, deps: DependencyList = []): UseDebounceReturn {
|
||||
export default function useDebounce(
|
||||
fn: Function,
|
||||
ms: number = 0,
|
||||
deps: DependencyList = []
|
||||
): UseDebounceReturn {
|
||||
const [isReady, cancel, reset] = useTimeoutFn(fn, ms);
|
||||
|
||||
useEffect(reset, deps);
|
||||
|
||||
@ -7,7 +7,9 @@ const isPrimitive = (val: any) => val !== Object(val);
|
||||
const useDeepCompareEffect = (effect: EffectCallback, deps: DependencyList) => {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
if (!(deps instanceof Array) || !deps.length) {
|
||||
console.warn('`useDeepCompareEffect` should not be used with no dependencies. Use React.useEffect instead.');
|
||||
console.warn(
|
||||
'`useDeepCompareEffect` should not be used with no dependencies. Use React.useEffect instead.'
|
||||
);
|
||||
}
|
||||
|
||||
if (deps.every(isPrimitive)) {
|
||||
|
||||
@ -1,6 +1,9 @@
|
||||
import { useState } from 'react';
|
||||
|
||||
const useDefault = <TStateType>(defaultValue: TStateType, initialValue: TStateType | (() => TStateType)) => {
|
||||
const useDefault = <TStateType>(
|
||||
defaultValue: TStateType,
|
||||
initialValue: TStateType | (() => TStateType)
|
||||
) => {
|
||||
const [value, setValue] = useState<TStateType | undefined | null>(initialValue);
|
||||
|
||||
if (value === undefined || value === null) {
|
||||
|
||||
@ -26,7 +26,10 @@ const defaultState: DropAreaState = {
|
||||
};
|
||||
*/
|
||||
|
||||
const createProcess = (options: DropAreaOptions, mounted: boolean) => (dataTransfer: DataTransfer, event) => {
|
||||
const createProcess = (options: DropAreaOptions, mounted: boolean) => (
|
||||
dataTransfer: DataTransfer,
|
||||
event
|
||||
) => {
|
||||
const uri = dataTransfer.getData('text/uri-list');
|
||||
|
||||
if (uri) {
|
||||
|
||||
@ -10,7 +10,9 @@ import {
|
||||
useRef,
|
||||
} from 'react';
|
||||
|
||||
export default function useEnsuredForwardedRef<T>(forwardedRef: MutableRefObject<T>): MutableRefObject<T> {
|
||||
export default function useEnsuredForwardedRef<T>(
|
||||
forwardedRef: MutableRefObject<T>
|
||||
): MutableRefObject<T> {
|
||||
const ensuredRef = useRef(forwardedRef && forwardedRef.current);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@ -24,7 +24,11 @@ const isListenerType2 = (target: any): target is ListenerType2 => {
|
||||
return !!target.on;
|
||||
};
|
||||
|
||||
type AddEventListener<T> = T extends ListenerType1 ? T['addEventListener'] : T extends ListenerType2 ? T['on'] : never;
|
||||
type AddEventListener<T> = T extends ListenerType1
|
||||
? T['addEventListener']
|
||||
: T extends ListenerType2
|
||||
? T['on']
|
||||
: never;
|
||||
|
||||
const useEvent = <T extends UseEventTarget>(
|
||||
name: Parameters<AddEventListener<T>>[0],
|
||||
|
||||
@ -2,7 +2,8 @@ import { useEffect } from 'react';
|
||||
|
||||
const useFavicon = (href: string) => {
|
||||
useEffect(() => {
|
||||
const link: HTMLLinkElement = document.querySelector("link[rel*='icon']") || document.createElement('link');
|
||||
const link: HTMLLinkElement =
|
||||
document.querySelector("link[rel*='icon']") || document.createElement('link');
|
||||
link.type = 'image/x-icon';
|
||||
link.rel = 'shortcut icon';
|
||||
link.href = href;
|
||||
|
||||
@ -4,11 +4,17 @@ import useIsomorphicLayoutEffect from './useIsomorphicLayoutEffect';
|
||||
import { noop, off, on } from './misc/util';
|
||||
|
||||
export interface FullScreenOptions {
|
||||
video?: RefObject<HTMLVideoElement & { webkitEnterFullscreen?: () => void; webkitExitFullscreen?: () => void }>;
|
||||
video?: RefObject<
|
||||
HTMLVideoElement & { webkitEnterFullscreen?: () => void; webkitExitFullscreen?: () => void }
|
||||
>;
|
||||
onClose?: (error?: Error) => void;
|
||||
}
|
||||
|
||||
const useFullscreen = (ref: RefObject<Element>, enabled: boolean, options: FullScreenOptions = {}): boolean => {
|
||||
const useFullscreen = (
|
||||
ref: RefObject<Element>,
|
||||
enabled: boolean,
|
||||
options: FullScreenOptions = {}
|
||||
): boolean => {
|
||||
const { video, onClose = noop } = options;
|
||||
const [isFullscreen, setIsFullscreen] = useState(enabled);
|
||||
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
import { useCallback, useRef } from 'react';
|
||||
import useUpdate from './useUpdate';
|
||||
|
||||
const useGetSetState = <T extends object>(initialState: T = {} as T): [() => T, (patch: Partial<T>) => void] => {
|
||||
const useGetSetState = <T extends object>(
|
||||
initialState: T = {} as T
|
||||
): [() => T, (patch: Partial<T>) => void] => {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
if (typeof initialState !== 'object') {
|
||||
console.error('useGetSetState initial state must be an object.');
|
||||
|
||||
@ -5,7 +5,11 @@ import { off, on } from './misc/util';
|
||||
const defaultEvents = ['mousemove', 'mousedown', 'resize', 'keydown', 'touchstart', 'wheel'];
|
||||
const oneMinute = 60e3;
|
||||
|
||||
const useIdle = (ms: number = oneMinute, initialState: boolean = false, events: string[] = defaultEvents): boolean => {
|
||||
const useIdle = (
|
||||
ms: number = oneMinute,
|
||||
initialState: boolean = false,
|
||||
events: string[] = defaultEvents
|
||||
): boolean => {
|
||||
const [state, setState] = useState<boolean>(initialState);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@ -4,7 +4,10 @@ const useIntersection = (
|
||||
ref: RefObject<HTMLElement>,
|
||||
options: IntersectionObserverInit
|
||||
): IntersectionObserverEntry | null => {
|
||||
const [intersectionObserverEntry, setIntersectionObserverEntry] = useState<IntersectionObserverEntry | null>(null);
|
||||
const [
|
||||
intersectionObserverEntry,
|
||||
setIntersectionObserverEntry,
|
||||
] = useState<IntersectionObserverEntry | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (ref.current && typeof IntersectionObserver === 'function') {
|
||||
|
||||
@ -21,7 +21,12 @@ const createKeyPredicate = (keyFilter: KeyFilter): KeyPredicate =>
|
||||
? () => true
|
||||
: () => false;
|
||||
|
||||
const useKey = (key: KeyFilter, fn: Handler = noop, opts: UseKeyOptions = {}, deps: DependencyList = [key]) => {
|
||||
const useKey = (
|
||||
key: KeyFilter,
|
||||
fn: Handler = noop,
|
||||
opts: UseKeyOptions = {},
|
||||
deps: DependencyList = [key]
|
||||
) => {
|
||||
const { event = 'keydown', target, options } = opts;
|
||||
const useMemoHandler = useMemo(() => {
|
||||
const predicate: KeyPredicate = createKeyPredicate(key);
|
||||
|
||||
@ -117,7 +117,10 @@ function useList<T>(initialList: IHookStateInitAction<T[]> = []): [T[], ListActi
|
||||
actions.set((curr: T[]) => curr.slice().sort(compareFn));
|
||||
},
|
||||
|
||||
filter: <S extends T>(callbackFn: (value: T, index: number, array: T[]) => value is S, thisArg?: any) => {
|
||||
filter: <S extends T>(
|
||||
callbackFn: (value: T, index: number, array: T[]) => value is S,
|
||||
thisArg?: any
|
||||
) => {
|
||||
actions.set((curr: T[]) => curr.slice().filter(callbackFn, thisArg));
|
||||
},
|
||||
|
||||
|
||||
@ -23,7 +23,11 @@ const useLocalStorage = <T>(
|
||||
throw new Error('useLocalStorage key may not be falsy');
|
||||
}
|
||||
|
||||
const deserializer = options ? (options.raw ? (value) => value : options.deserializer) : JSON.parse;
|
||||
const deserializer = options
|
||||
? options.raw
|
||||
? (value) => value
|
||||
: options.deserializer
|
||||
: JSON.parse;
|
||||
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
const [state, setState] = useState<T | undefined>(() => {
|
||||
@ -49,7 +53,8 @@ const useLocalStorage = <T>(
|
||||
const set: Dispatch<SetStateAction<T | undefined>> = useCallback(
|
||||
(valOrFunc) => {
|
||||
try {
|
||||
const newState = typeof valOrFunc === 'function' ? (valOrFunc as Function)(state) : valOrFunc;
|
||||
const newState =
|
||||
typeof valOrFunc === 'function' ? (valOrFunc as Function)(state) : valOrFunc;
|
||||
if (typeof newState === 'undefined') return;
|
||||
let value: string;
|
||||
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
import { RefObject, useEffect, useRef } from 'react';
|
||||
import { isBrowser, off, on } from './misc/util';
|
||||
|
||||
export function getClosestBody(el: Element | HTMLElement | HTMLIFrameElement | null): HTMLElement | null {
|
||||
export function getClosestBody(
|
||||
el: Element | HTMLElement | HTMLIFrameElement | null
|
||||
): HTMLElement | null {
|
||||
if (!el) {
|
||||
return null;
|
||||
} else if (el.tagName === 'BODY') {
|
||||
@ -32,7 +34,10 @@ export interface BodyInfoItem {
|
||||
}
|
||||
|
||||
const isIosDevice =
|
||||
isBrowser && 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();
|
||||
|
||||
@ -60,7 +65,10 @@ export default !doc
|
||||
body.style.overflow = 'hidden';
|
||||
}
|
||||
} else {
|
||||
bodies.set(body, { counter: bodyInfo.counter + 1, initialOverflow: bodyInfo.initialOverflow });
|
||||
bodies.set(body, {
|
||||
counter: bodyInfo.counter + 1,
|
||||
initialOverflow: bodyInfo.initialOverflow,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@ -80,7 +88,10 @@ export default !doc
|
||||
body.style.overflow = bodyInfo.initialOverflow;
|
||||
}
|
||||
} else {
|
||||
bodies.set(body, { counter: bodyInfo.counter - 1, initialOverflow: bodyInfo.initialOverflow });
|
||||
bodies.set(body, {
|
||||
counter: bodyInfo.counter - 1,
|
||||
initialOverflow: bodyInfo.initialOverflow,
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -7,7 +7,10 @@ export type UseMeasureRect = Pick<
|
||||
'x' | 'y' | 'top' | 'left' | 'right' | 'bottom' | 'height' | 'width'
|
||||
>;
|
||||
export type UseMeasureRef<E extends HTMLElement = HTMLElement> = (element: E) => void;
|
||||
export type UseMeasureResult<E extends HTMLElement = HTMLElement> = [UseMeasureRef<E>, UseMeasureRect];
|
||||
export type UseMeasureResult<E extends HTMLElement = HTMLElement> = [
|
||||
UseMeasureRef<E>,
|
||||
UseMeasureRect
|
||||
];
|
||||
|
||||
const defaultState: UseMeasureRect = {
|
||||
x: 0,
|
||||
|
||||
@ -2,7 +2,9 @@ import { useEffect, useState } from 'react';
|
||||
import { isBrowser } from './misc/util';
|
||||
|
||||
const useMedia = (query: string, defaultState: boolean = false) => {
|
||||
const [state, setState] = useState(isBrowser ? () => window.matchMedia(query).matches : defaultState);
|
||||
const [state, setState] = useState(
|
||||
isBrowser ? () => window.matchMedia(query).matches : defaultState
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
let mounted = true;
|
||||
|
||||
@ -13,7 +13,12 @@ const useMediaDevices = () => {
|
||||
.then((devices) => {
|
||||
if (mounted) {
|
||||
setState({
|
||||
devices: devices.map(({ deviceId, groupId, kind, label }) => ({ deviceId, groupId, kind, label })),
|
||||
devices: devices.map(({ deviceId, groupId, kind, label }) => ({
|
||||
deviceId,
|
||||
groupId,
|
||||
kind,
|
||||
label,
|
||||
})),
|
||||
});
|
||||
}
|
||||
})
|
||||
@ -34,4 +39,6 @@ const useMediaDevices = () => {
|
||||
|
||||
const useMediaDevicesMock = () => ({});
|
||||
|
||||
export default typeof navigator === 'object' && !!navigator.mediaDevices ? useMediaDevices : useMediaDevicesMock;
|
||||
export default typeof navigator === 'object' && !!navigator.mediaDevices
|
||||
? useMediaDevices
|
||||
: useMediaDevicesMock;
|
||||
|
||||
@ -11,9 +11,15 @@ export type UseMediatedStateReturn<S = any> = [S, Dispatch<SetStateAction<S>>];
|
||||
export function useMediatedState<S = undefined>(
|
||||
mediator: StateMediator<S | undefined>
|
||||
): UseMediatedStateReturn<S | undefined>;
|
||||
export function useMediatedState<S = any>(mediator: StateMediator<S>, initialState: S): UseMediatedStateReturn<S>;
|
||||
export function useMediatedState<S = any>(
|
||||
mediator: StateMediator<S>,
|
||||
initialState: S
|
||||
): UseMediatedStateReturn<S>;
|
||||
|
||||
export function useMediatedState<S = any>(mediator: StateMediator<S>, initialState?: S): UseMediatedStateReturn<S> {
|
||||
export function useMediatedState<S = any>(
|
||||
mediator: StateMediator<S>,
|
||||
initialState?: S
|
||||
): UseMediatedStateReturn<S> {
|
||||
const mediatorFn = useRef(mediator);
|
||||
|
||||
const [state, setMediatedState] = useState<S>(initialState!);
|
||||
|
||||
@ -15,7 +15,10 @@ type WrappedMethods<M> = {
|
||||
[P in keyof M]: (...payload: any) => void;
|
||||
};
|
||||
|
||||
const useMethods = <M, T>(createMethods: CreateMethods<M, T>, initialState: T): [T, WrappedMethods<M>] => {
|
||||
const useMethods = <M, T>(
|
||||
createMethods: CreateMethods<M, T>,
|
||||
initialState: T
|
||||
): [T, WrappedMethods<M>] => {
|
||||
const reducer = useMemo<Reducer<T, Action>>(
|
||||
() => (reducerState: T, action: Action) => {
|
||||
return createMethods(reducerState)[action.type](...action.payload);
|
||||
|
||||
@ -2,9 +2,15 @@ import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { StateValidator, UseStateValidatorReturn, ValidityState } from './useStateValidator';
|
||||
|
||||
export type MultiStateValidatorStates = any[] | { [p: string]: any } | { [p: number]: any };
|
||||
export type MultiStateValidator<V extends ValidityState, S extends MultiStateValidatorStates> = StateValidator<V, S>;
|
||||
export type MultiStateValidator<
|
||||
V extends ValidityState,
|
||||
S extends MultiStateValidatorStates
|
||||
> = StateValidator<V, S>;
|
||||
|
||||
export function useMultiStateValidator<V extends ValidityState, S extends MultiStateValidatorStates>(
|
||||
export function useMultiStateValidator<
|
||||
V extends ValidityState,
|
||||
S extends MultiStateValidatorStates
|
||||
>(
|
||||
states: S,
|
||||
validator: MultiStateValidator<V, S>,
|
||||
initialValidity: V = [undefined] as V
|
||||
|
||||
@ -8,7 +8,15 @@ export interface INetworkInformation extends EventTarget {
|
||||
readonly effectiveType: 'slow-2g' | '2g' | '3g' | '4g';
|
||||
readonly rtt: number;
|
||||
readonly saveData: boolean;
|
||||
readonly type: 'bluetooth' | 'cellular' | 'ethernet' | 'none' | 'wifi' | 'wimax' | 'other' | 'unknown';
|
||||
readonly type:
|
||||
| 'bluetooth'
|
||||
| 'cellular'
|
||||
| 'ethernet'
|
||||
| 'none'
|
||||
| 'wifi'
|
||||
| 'wimax'
|
||||
| 'other'
|
||||
| 'unknown';
|
||||
|
||||
onChange: (event: Event) => void;
|
||||
}
|
||||
@ -68,9 +76,11 @@ export interface IUseNetworkState {
|
||||
}
|
||||
|
||||
const nav:
|
||||
| (Navigator & Partial<Record<'connection' | 'mozConnection' | 'webkitConnection', INetworkInformation>>)
|
||||
| (Navigator &
|
||||
Partial<Record<'connection' | 'mozConnection' | 'webkitConnection', INetworkInformation>>)
|
||||
| undefined = navigator;
|
||||
const conn: INetworkInformation | undefined = nav && (nav.connection || nav.mozConnection || nav.webkitConnection);
|
||||
const conn: INetworkInformation | undefined =
|
||||
nav && (nav.connection || nav.mozConnection || nav.webkitConnection);
|
||||
|
||||
function getConnectionState(previousState?: IUseNetworkState): IUseNetworkState {
|
||||
const online = nav?.onLine;
|
||||
@ -89,7 +99,9 @@ function getConnectionState(previousState?: IUseNetworkState): IUseNetworkState
|
||||
};
|
||||
}
|
||||
|
||||
export default function useNetworkState(initialState?: IHookStateInitAction<IUseNetworkState>): IUseNetworkState {
|
||||
export default function useNetworkState(
|
||||
initialState?: IHookStateInitAction<IUseNetworkState>
|
||||
): IUseNetworkState {
|
||||
const [state, setState] = useState(initialState ?? getConnectionState);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@ -5,7 +5,10 @@ export type Predicate<T> = (prev: T | undefined, next: T) => boolean;
|
||||
|
||||
const strictEquals = <T>(prev: T | undefined, next: T) => prev === next;
|
||||
|
||||
export default function usePreviousDistinct<T>(value: T, compare: Predicate<T> = strictEquals): T | undefined {
|
||||
export default function usePreviousDistinct<T>(
|
||||
value: T,
|
||||
compare: Predicate<T> = strictEquals
|
||||
): T | undefined {
|
||||
const prevRef = useRef<T>();
|
||||
const curRef = useRef<T>(value);
|
||||
const isFirstMount = useFirstMountState();
|
||||
|
||||
@ -2,7 +2,10 @@ import { useCallback, useEffect, useMemo, useRef } from 'react';
|
||||
|
||||
export type RafLoopReturns = [() => void, () => void, () => boolean];
|
||||
|
||||
export default function useRafLoop(callback: FrameRequestCallback, initiallyActive = true): RafLoopReturns {
|
||||
export default function useRafLoop(
|
||||
callback: FrameRequestCallback,
|
||||
initiallyActive = true
|
||||
): RafLoopReturns {
|
||||
const raf = useRef<number | null>(null);
|
||||
const rafActivity = useRef<boolean>(false);
|
||||
const rafCallback = useRef(callback);
|
||||
|
||||
@ -28,7 +28,9 @@ export interface ScratchSensorState {
|
||||
elY?: number;
|
||||
}
|
||||
|
||||
const useScratch = (params: ScratchSensorParams = {}): [(el: HTMLElement | null) => void, ScratchSensorState] => {
|
||||
const useScratch = (
|
||||
params: ScratchSensorParams = {}
|
||||
): [(el: HTMLElement | null) => void, ScratchSensorState] => {
|
||||
const { disabled } = params;
|
||||
const paramsRef = useLatest(params);
|
||||
const [state, setState] = useState<ScratchSensorState>({ isScratching: false });
|
||||
@ -155,7 +157,10 @@ const useScratch = (params: ScratchSensorParams = {}): [(el: HTMLElement | null)
|
||||
};
|
||||
|
||||
export interface ScratchSensorProps extends ScratchSensorParams {
|
||||
children: (state: ScratchSensorState, ref: (el: HTMLElement | null) => void) => React.ReactElement<any>;
|
||||
children: (
|
||||
state: ScratchSensorState,
|
||||
ref: (el: HTMLElement | null) => void
|
||||
) => React.ReactElement<any>;
|
||||
}
|
||||
|
||||
export const ScratchSensor: FC<ScratchSensorProps> = (props) => {
|
||||
|
||||
@ -1,7 +1,11 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { isBrowser } from './misc/util';
|
||||
|
||||
const useSessionStorage = <T>(key: string, initialValue?: T, raw?: boolean): [T, (value: T) => void] => {
|
||||
const useSessionStorage = <T>(
|
||||
key: string,
|
||||
initialValue?: T,
|
||||
raw?: boolean
|
||||
): [T, (value: T) => void] => {
|
||||
if (!isBrowser) {
|
||||
return [initialValue as T, () => {}];
|
||||
}
|
||||
|
||||
@ -16,7 +16,8 @@ const useSet = <K>(initialSet = new Set<K>()): [Set<K>, Actions<K>] => {
|
||||
|
||||
const stableActions = useMemo<StableActions<K>>(() => {
|
||||
const add = (item: K) => setSet((prevSet) => new Set([...Array.from(prevSet), item]));
|
||||
const remove = (item: K) => setSet((prevSet) => new Set(Array.from(prevSet).filter((i) => i !== item)));
|
||||
const remove = (item: K) =>
|
||||
setSet((prevSet) => new Set(Array.from(prevSet).filter((i) => i !== item)));
|
||||
const toggle = (item: K) =>
|
||||
setSet((prevSet) =>
|
||||
prevSet.has(item)
|
||||
|
||||
@ -6,7 +6,9 @@ const useSetState = <T extends object>(
|
||||
const [state, set] = useState<T>(initialState);
|
||||
const setState = useCallback(
|
||||
(patch) => {
|
||||
set((prevState) => Object.assign({}, prevState, patch instanceof Function ? patch(prevState) : patch));
|
||||
set((prevState) =>
|
||||
Object.assign({}, prevState, patch instanceof Function ? patch(prevState) : patch)
|
||||
);
|
||||
},
|
||||
[set]
|
||||
);
|
||||
|
||||
@ -9,7 +9,9 @@ const shallowEqualDepsList = (prevDeps: DependencyList, nextDeps: DependencyList
|
||||
const useShallowCompareEffect = (effect: EffectCallback, deps: DependencyList) => {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
if (!(deps instanceof Array) || !deps.length) {
|
||||
console.warn('`useShallowCompareEffect` should not be used with no dependencies. Use React.useEffect instead.');
|
||||
console.warn(
|
||||
'`useShallowCompareEffect` should not be used with no dependencies. Use React.useEffect instead.'
|
||||
);
|
||||
}
|
||||
|
||||
if (deps.every(isPrimitive)) {
|
||||
|
||||
@ -17,7 +17,10 @@ const useSize = (
|
||||
{ width = Infinity, height = Infinity }: Partial<State> = {}
|
||||
): [React.ReactElement<any>, State] => {
|
||||
if (!isBrowser) {
|
||||
return [typeof element === 'function' ? element({ width, height }) : element, { width, height }];
|
||||
return [
|
||||
typeof element === 'function' ? element({ width, height }) : element,
|
||||
{ width, height },
|
||||
];
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
|
||||
@ -20,7 +20,8 @@ export interface SpeechOptions {
|
||||
volume?: number;
|
||||
}
|
||||
|
||||
const voices = isBrowser && 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>({
|
||||
|
||||
@ -42,7 +42,10 @@ export default function useStateList<T>(stateSet: T[] = []): UseStateListReturn<
|
||||
// it gives the ability to travel through the left and right borders.
|
||||
// 4ex: if list contains 5 elements, attempt to set index 9 will bring use to 5th element
|
||||
// in case of negative index it will start counting from the right, so -17 will bring us to 4th element
|
||||
index.current = newIndex >= 0 ? newIndex % stateSet.length : stateSet.length + (newIndex % stateSet.length);
|
||||
index.current =
|
||||
newIndex >= 0
|
||||
? newIndex % stateSet.length
|
||||
: stateSet.length + (newIndex % stateSet.length);
|
||||
update();
|
||||
},
|
||||
setState: (state: T) => {
|
||||
|
||||
@ -104,7 +104,10 @@ export function useStateWithHistory<S, I extends S>(
|
||||
}
|
||||
|
||||
innerSetState(() => {
|
||||
historyPosition.current = Math.min(historyPosition.current + amount, history.current.length - 1);
|
||||
historyPosition.current = Math.min(
|
||||
historyPosition.current + amount,
|
||||
history.current.length - 1
|
||||
);
|
||||
|
||||
return history.current[historyPosition.current];
|
||||
});
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { Reducer, useReducer } from 'react';
|
||||
|
||||
const toggleReducer = (state: boolean, nextValue?: any) => (typeof nextValue === 'boolean' ? nextValue : !state);
|
||||
const toggleReducer = (state: boolean, nextValue?: any) =>
|
||||
typeof nextValue === 'boolean' ? nextValue : !state;
|
||||
|
||||
const useToggle = (initialValue: boolean): [boolean, (nextValue?: any) => void] => {
|
||||
return useReducer<Reducer<boolean, any>>(toggleReducer, initialValue);
|
||||
|
||||
@ -5,7 +5,11 @@ export type VibrationPattern = number | number[];
|
||||
|
||||
const isVibrationApiSupported = typeof navigator === 'object' && 'vibrate' in navigator;
|
||||
|
||||
function useVibrate(enabled: boolean = true, pattern: VibrationPattern = [1000, 1000], loop: boolean = true): void {
|
||||
function useVibrate(
|
||||
enabled: boolean = true,
|
||||
pattern: VibrationPattern = [1000, 1000],
|
||||
loop: boolean = true
|
||||
): void {
|
||||
useEffect(() => {
|
||||
let interval;
|
||||
|
||||
@ -13,7 +17,8 @@ function useVibrate(enabled: boolean = true, pattern: VibrationPattern = [1000,
|
||||
navigator.vibrate(pattern);
|
||||
|
||||
if (loop) {
|
||||
const duration = pattern instanceof Array ? pattern.reduce((a, b) => a + b) : (pattern as number);
|
||||
const duration =
|
||||
pattern instanceof Array ? pattern.reduce((a, b) => a + b) : (pattern as number);
|
||||
|
||||
interval = setInterval(() => {
|
||||
navigator.vibrate(pattern);
|
||||
|
||||
@ -27,8 +27,10 @@ const Demo = () => {
|
||||
<div>
|
||||
<strong>Battery sensor</strong>: <span>supported</span> <br />
|
||||
<strong>Battery state</strong>: <span>fetched</span> <br />
|
||||
<strong>Charge level</strong>: <span>{(batteryState.level * 100).toFixed(0)}%</span> <br />
|
||||
<strong>Charging</strong>: <span>{batteryState.charging ? 'yes' : 'no'}</span> <br />
|
||||
<strong>Charge level</strong>: {' '}
|
||||
<span>{(batteryState.level * 100).toFixed(0)}%</span> <br />
|
||||
<strong>Charging</strong>: <span>{batteryState.charging ? 'yes' : 'no'}</span>{' '}
|
||||
<br />
|
||||
<strong>Charging time</strong>:
|
||||
<span>{batteryState.chargingTime ? batteryState.chargingTime : 'finished'}</span> <br />
|
||||
<strong>Discharging time</strong>: <span>{batteryState.dischargingTime}</span>
|
||||
|
||||
@ -21,7 +21,8 @@ const Demo = () => {
|
||||
return (
|
||||
<CenterStory>
|
||||
<p>
|
||||
Press some keys on your keyboard, <code style={{ color: 'tomato' }}>r</code> key resets the list
|
||||
Press some keys on your keyboard, <code style={{ color: 'tomato' }}>r</code> key resets the
|
||||
list
|
||||
</p>
|
||||
<pre>{JSON.stringify(list, null, 4)}</pre>
|
||||
</CenterStory>
|
||||
|
||||
@ -32,8 +32,7 @@ const Demo = () => {
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
}}>
|
||||
<video
|
||||
ref={videoRef}
|
||||
style={{ width: '70%' }}
|
||||
|
||||
@ -10,5 +10,5 @@ const Demo = () => {
|
||||
};
|
||||
|
||||
storiesOf('Sensors/useGeolocation', module)
|
||||
.add('Docs', () => <ShowDocs md={require('../docs/useGeolocation.md')} />)
|
||||
.add('Docs', () => <ShowDocs md={require('../docs/useGeoLocation.md')} />)
|
||||
.add('Demo', () => <Demo />);
|
||||
|
||||
@ -9,7 +9,12 @@ const Demo = () => {
|
||||
|
||||
return (
|
||||
<div>
|
||||
Idle delay ms: <input type="number" value={idleDelay} onChange={({ target }) => setIdleDelay(+target.value)} />
|
||||
Idle delay ms:{' '}
|
||||
<input
|
||||
type="number"
|
||||
value={idleDelay}
|
||||
onChange={({ target }) => setIdleDelay(+target.value)}
|
||||
/>
|
||||
<div>User is idle: {isIdle ? 'Yes' : 'No'}</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -28,8 +28,7 @@ const Demo = () => {
|
||||
height: '400px',
|
||||
backgroundColor: 'whitesmoke',
|
||||
overflow: 'scroll',
|
||||
}}
|
||||
>
|
||||
}}>
|
||||
Scroll me
|
||||
<Spacer />
|
||||
<div
|
||||
@ -39,8 +38,7 @@ const Demo = () => {
|
||||
height: '100px',
|
||||
padding: '20px',
|
||||
backgroundColor: 'palegreen',
|
||||
}}
|
||||
>
|
||||
}}>
|
||||
{intersection && intersection.intersectionRatio < 1 ? 'Obscured' : 'Fully in view'}
|
||||
</div>
|
||||
<Spacer />
|
||||
|
||||
@ -7,8 +7,8 @@ import ShowDocs from './util/ShowDocs';
|
||||
const Demo = () => {
|
||||
const [count, setCount] = React.useState(0);
|
||||
|
||||
const increment = () => setCount(currentCount => ++currentCount);
|
||||
const decrement = () => setCount(currentCount => --currentCount);
|
||||
const increment = () => setCount((currentCount) => ++currentCount);
|
||||
const decrement = () => setCount((currentCount) => --currentCount);
|
||||
const reset = () => setCount(() => 0);
|
||||
|
||||
useKey(']', increment);
|
||||
@ -19,7 +19,8 @@ const Demo = () => {
|
||||
<CenterStory>
|
||||
<style dangerouslySetInnerHTML={{ __html: `code {color: red}` }} />
|
||||
<p>
|
||||
Try pressing <code>[</code>, <code>]</code>, and <code>r</code> to see the count incremented and decremented.
|
||||
Try pressing <code>[</code>, <code>]</code>, and <code>r</code> to see the count incremented
|
||||
and decremented.
|
||||
</p>
|
||||
<p>Count: {count}</p>
|
||||
</CenterStory>
|
||||
@ -28,7 +29,7 @@ const Demo = () => {
|
||||
|
||||
const CounterDemo = () => {
|
||||
const [count, setCount] = React.useState(0);
|
||||
const increment = () => setCount(currentCount => ++currentCount);
|
||||
const increment = () => setCount((currentCount) => ++currentCount);
|
||||
useKey('ArrowUp', increment);
|
||||
|
||||
return <div>Press arrow up: {count}</div>;
|
||||
|
||||
@ -7,8 +7,9 @@ import ShowDocs from './util/ShowDocs';
|
||||
const keys = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0'];
|
||||
|
||||
const Demo = () => {
|
||||
const states = [];
|
||||
const states: boolean[] = [];
|
||||
for (const key of keys) {
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
states.push(useKeyPress(key)[0]);
|
||||
}
|
||||
|
||||
@ -17,7 +18,10 @@ const Demo = () => {
|
||||
<div style={{ textAlign: 'center' }}>
|
||||
Try pressing numbers
|
||||
<br />
|
||||
{states.reduce((s, pressed, index) => s + (pressed ? (s ? ' + ' : '') + keys[index] : ''), '')}
|
||||
{states.reduce(
|
||||
(s, pressed, index) => s + (pressed ? (s ? ' + ' : '') + keys[index] : ''),
|
||||
''
|
||||
)}
|
||||
</div>
|
||||
</CenterStory>
|
||||
);
|
||||
|
||||
@ -8,11 +8,11 @@ const Demo = () => {
|
||||
|
||||
const increment = () => {
|
||||
console.log('INCREMENT');
|
||||
setCount(currentCount => ++currentCount);
|
||||
setCount((currentCount) => ++currentCount);
|
||||
};
|
||||
const decrement = () => {
|
||||
console.log('DECREMENT');
|
||||
setCount(currentCount => --currentCount);
|
||||
setCount((currentCount) => --currentCount);
|
||||
};
|
||||
const reset = () => setCount(() => 0);
|
||||
|
||||
@ -24,7 +24,8 @@ const Demo = () => {
|
||||
<div>
|
||||
<style dangerouslySetInnerHTML={{ __html: `code {color: red}` }} />
|
||||
<p>
|
||||
Try pressing <code>[</code>, <code>]</code>, and <code>r</code> to see the count incremented and decremented.
|
||||
Try pressing <code>[</code>, <code>]</code>, and <code>r</code> to see the count incremented
|
||||
and decremented.
|
||||
</p>
|
||||
<p>Count: {count}</p>
|
||||
</div>
|
||||
@ -36,11 +37,11 @@ const DemoKeyboardJs = () => {
|
||||
|
||||
const increment = () => {
|
||||
console.log('INCREMENT');
|
||||
setCount(currentCount => ++currentCount);
|
||||
setCount((currentCount) => ++currentCount);
|
||||
};
|
||||
const decrement = () => {
|
||||
console.log('DECREMENT');
|
||||
setCount(currentCount => --currentCount);
|
||||
setCount((currentCount) => --currentCount);
|
||||
};
|
||||
const reset = () => setCount(() => 0);
|
||||
|
||||
@ -52,8 +53,8 @@ const DemoKeyboardJs = () => {
|
||||
<div>
|
||||
<style dangerouslySetInnerHTML={{ __html: `code {color: red}` }} />
|
||||
<p>
|
||||
Try pressing <code>q + [</code>, <code>q + ]</code>, and <code>q + r</code> to see the count incremented and
|
||||
decremented.
|
||||
Try pressing <code>q + [</code>, <code>q + ]</code>, and <code>q + r</code> to see the count
|
||||
incremented and decremented.
|
||||
</p>
|
||||
<p>Count: {count}</p>
|
||||
</div>
|
||||
|
||||
@ -12,7 +12,10 @@ const Demo = ({ combo }) => {
|
||||
<CenterStory>
|
||||
<div style={{ textAlign: 'center' }}>
|
||||
Press{' '}
|
||||
<code style={{ color: 'red', background: '#f6f6f6', padding: '3px 6px', borderRadius: '3px' }}>{combo}</code>{' '}
|
||||
<code
|
||||
style={{ color: 'red', background: '#f6f6f6', padding: '3px 6px', borderRadius: '3px' }}>
|
||||
{combo}
|
||||
</code>{' '}
|
||||
combo
|
||||
<br />
|
||||
<br />
|
||||
|
||||
@ -4,13 +4,10 @@ import { useList } from '../src';
|
||||
import ShowDocs from './util/ShowDocs';
|
||||
|
||||
const Demo = () => {
|
||||
const [list, { set, push, updateAt, insertAt, update, updateFirst, sort, filter, removeAt, clear, reset }] = useList([
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
]);
|
||||
const [
|
||||
list,
|
||||
{ set, push, updateAt, insertAt, update, updateFirst, sort, filter, removeAt, clear, reset },
|
||||
] = useList([1, 2, 3, 4, 5]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
@ -64,11 +61,17 @@ const UpsertDemo = () => {
|
||||
<div style={{ display: 'inline-flex', flexDirection: 'column' }}>
|
||||
{list.map((item, index) => (
|
||||
<div key={item.id}>
|
||||
<input value={item.text} onChange={(e) => upsert(upsertPredicate, { ...item, text: e.target.value })} />
|
||||
<input
|
||||
value={item.text}
|
||||
onChange={(e) => upsert(upsertPredicate, { ...item, text: e.target.value })}
|
||||
/>
|
||||
<button onClick={() => removeAt(index)}>Remove</button>
|
||||
</div>
|
||||
))}
|
||||
<button onClick={() => upsert(upsertPredicate, { id: (list.length + 1).toString(), text: '' })}>Add item</button>
|
||||
<button
|
||||
onClick={() => upsert(upsertPredicate, { id: (list.length + 1).toString(), text: '' })}>
|
||||
Add item
|
||||
</button>
|
||||
<button onClick={() => reset()}>Reset</button>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -3,7 +3,7 @@ import * as React from 'react';
|
||||
import { useLocation } from '../src';
|
||||
import ShowDocs from './util/ShowDocs';
|
||||
|
||||
const go = (page) => history.pushState({}, '', page);
|
||||
const go = (page) => window.history.pushState({}, '', page);
|
||||
|
||||
const Demo = () => {
|
||||
const state = useLocation();
|
||||
|
||||
@ -44,7 +44,9 @@ const IframeComponent = () => {
|
||||
<button onClick={() => toggleMainLocked()} style={{ position: 'fixed', left: 0, top: 0 }}>
|
||||
{mainLocked ? 'Unlock' : 'Lock'} main window scroll
|
||||
</button>
|
||||
<button onClick={() => toggleIframeLocked()} style={{ position: 'fixed', left: 0, top: 64 }}>
|
||||
<button
|
||||
onClick={() => toggleIframeLocked()}
|
||||
style={{ position: 'fixed', left: 0, top: 64 }}>
|
||||
{iframeLocked ? 'Unlock' : 'Lock'} iframe window scroll
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@ -19,8 +19,7 @@ const Demo: React.FC<any> = () => {
|
||||
width: '400px',
|
||||
height: '400px',
|
||||
backgroundColor: 'whitesmoke',
|
||||
}}
|
||||
>
|
||||
}}>
|
||||
<span
|
||||
style={{
|
||||
position: 'absolute',
|
||||
@ -28,8 +27,7 @@ const Demo: React.FC<any> = () => {
|
||||
top: `${state.elY}px`,
|
||||
pointerEvents: 'none',
|
||||
transform: 'scale(4)',
|
||||
}}
|
||||
>
|
||||
}}>
|
||||
🐭
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@ -20,8 +20,7 @@ const Demo: React.FC<any> = ({ whenHovered, bound }) => {
|
||||
width: '400px',
|
||||
height: '400px',
|
||||
backgroundColor: 'whitesmoke',
|
||||
}}
|
||||
>
|
||||
}}>
|
||||
<span
|
||||
style={{
|
||||
position: 'absolute',
|
||||
@ -29,8 +28,7 @@ const Demo: React.FC<any> = ({ whenHovered, bound }) => {
|
||||
top: `${state.elY}px`,
|
||||
pointerEvents: 'none',
|
||||
transform: 'scale(4)',
|
||||
}}
|
||||
>
|
||||
}}>
|
||||
🐭
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@ -41,7 +41,9 @@ const Demo = () => {
|
||||
setState3((ev.target.value as unknown) as number);
|
||||
}}
|
||||
/>
|
||||
{isValid !== undefined && <span style={{ marginLeft: 24 }}>{isValid ? 'Valid!' : 'Invalid'}</span>}
|
||||
{isValid !== undefined && (
|
||||
<span style={{ marginLeft: 24 }}>{isValid ? 'Valid!' : 'Invalid'}</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@ -9,12 +9,14 @@ const Demo = () => {
|
||||
|
||||
useEffect(() => {
|
||||
console.log(state);
|
||||
}, [state])
|
||||
}, [state]);
|
||||
|
||||
return <div>
|
||||
<div>Since JSON do not output `undefined` fields look the console to see whole the state</div>
|
||||
<pre>{JSON.stringify(state, null, 2)}</pre>
|
||||
</div>;
|
||||
return (
|
||||
<div>
|
||||
<div>Since JSON do not output `undefined` fields look the console to see whole the state</div>
|
||||
<pre>{JSON.stringify(state, null, 2)}</pre>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
storiesOf('Sensors/useNetworkState', module)
|
||||
|
||||
@ -22,8 +22,7 @@ const Demo = () => {
|
||||
onClick={() => {
|
||||
isActive() ? loopStop() : loopStart();
|
||||
update();
|
||||
}}
|
||||
>
|
||||
}}>
|
||||
{isActive() ? 'STOP' : 'START'}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@ -18,8 +18,7 @@ const Demo = () => {
|
||||
height: '400px',
|
||||
backgroundColor: 'whitesmoke',
|
||||
overflow: 'scroll',
|
||||
}}
|
||||
>
|
||||
}}>
|
||||
<div style={{ width: '2000px', height: '2000px' }}>Scroll me</div>
|
||||
</div>
|
||||
</>
|
||||
|
||||
@ -8,7 +8,9 @@ const Demo = () => {
|
||||
|
||||
return (
|
||||
<div>
|
||||
{sbw === undefined ? `DOM is not ready yet, SBW detection delayed` : `Browser's scrollbar width is ${sbw}px`}
|
||||
{sbw === undefined
|
||||
? `DOM is not ready yet, SBW detection delayed`
|
||||
: `Browser's scrollbar width is ${sbw}px`}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@ -18,8 +18,7 @@ const Demo = () => {
|
||||
height: '400px',
|
||||
backgroundColor: 'whitesmoke',
|
||||
overflow: 'scroll',
|
||||
}}
|
||||
>
|
||||
}}>
|
||||
<div style={{ width: '2000px', height: '2000px' }}>Scroll me</div>
|
||||
</div>
|
||||
</>
|
||||
|
||||
@ -10,17 +10,21 @@ const Demo = () => {
|
||||
<div>
|
||||
<div>edit: {edit || '🤷♂️'}</div>
|
||||
<div>
|
||||
<button onClick={() => history.pushState({}, '', location.pathname + '?edit=123')}>
|
||||
<button
|
||||
onClick={() => window.history.pushState({}, '', window.location.pathname + '?edit=123')}>
|
||||
Edit post 123 (?edit=123)
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<button onClick={() => history.pushState({}, '', location.pathname + '?edit=999')}>
|
||||
<button
|
||||
onClick={() => window.history.pushState({}, '', window.location.pathname + '?edit=999')}>
|
||||
Edit post 999 (?edit=999)
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<button onClick={() => history.pushState({}, '', location.pathname)}>Close modal</button>
|
||||
<button onClick={() => window.history.pushState({}, '', window.location.pathname)}>
|
||||
Close modal
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -16,8 +16,7 @@ const Demo = () => {
|
||||
setState((prevState) => ({
|
||||
count: prevState.count === undefined ? 0 : prevState.count + 1,
|
||||
}));
|
||||
}}
|
||||
>
|
||||
}}>
|
||||
increment
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@ -15,7 +15,13 @@ const Demo = () => {
|
||||
<div>
|
||||
<div ref={ref} style={{ position: 'relative', background: 'yellow', padding: 4 }}>
|
||||
<p style={{ margin: 0, textAlign: 'center' }}>Slide me</p>
|
||||
<div style={{ position: 'absolute', top: 0, left: 100 * state.value + '%', transform: 'scale(2)' }}>
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 100 * state.value + '%',
|
||||
transform: 'scale(2)',
|
||||
}}>
|
||||
{state.isSliding ? '🏂' : '🎿'}
|
||||
</div>
|
||||
</div>
|
||||
@ -30,9 +36,17 @@ const DemoVertical = () => {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div ref={ref} style={{ position: 'relative', background: 'yellow', padding: 4, width: 30, height: 400 }}>
|
||||
<div
|
||||
ref={ref}
|
||||
style={{ position: 'relative', background: 'yellow', padding: 4, width: 30, height: 400 }}>
|
||||
<p style={{ margin: 0, textAlign: 'center' }}>Slide me</p>
|
||||
<div style={{ position: 'absolute', left: 0, top: 100 * state.value + '%', transform: 'scale(2)' }}>
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
left: 0,
|
||||
top: 100 * state.value + '%',
|
||||
transform: 'scale(2)',
|
||||
}}>
|
||||
{state.isSliding ? '🏂' : '🎿'}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -21,7 +21,9 @@ const Demo = () => {
|
||||
<button onClick={() => next()}>next</button>
|
||||
<br />
|
||||
<input type="text" ref={indexInput} style={{ width: 120 }} />
|
||||
<button onClick={() => setStateAt((indexInput.current!.value as unknown) as number)}>set state by index</button>
|
||||
<button onClick={() => setStateAt((indexInput.current!.value as unknown) as number)}>
|
||||
set state by index
|
||||
</button>
|
||||
<br />
|
||||
<input type="text" ref={stateInput} style={{ width: 120 }} />
|
||||
<button onClick={() => setState(stateInput.current!.value)}> set state by value</button>
|
||||
|
||||
@ -3,7 +3,8 @@ import * as React from 'react';
|
||||
import useStateValidator from '../src/useStateValidator';
|
||||
import ShowDocs from './util/ShowDocs';
|
||||
|
||||
const DemoStateValidator = (s) => [s === '' ? undefined : (s * 1) % 2 === 0] as [boolean | undefined];
|
||||
const DemoStateValidator = (s) =>
|
||||
[s === '' ? undefined : (s * 1) % 2 === 0] as [boolean | undefined];
|
||||
const Demo = () => {
|
||||
const [state, setState] = React.useState<number>(0);
|
||||
const [[isValid]] = useStateValidator(state, DemoStateValidator);
|
||||
|
||||
@ -24,7 +24,7 @@ const Demo = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
history.back(stepSize);
|
||||
window.history.back(stepSize);
|
||||
},
|
||||
[history, stepSize]
|
||||
);
|
||||
@ -35,7 +35,7 @@ const Demo = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
history.forward(stepSize);
|
||||
window.history.forward(stepSize);
|
||||
},
|
||||
[history, stepSize]
|
||||
);
|
||||
@ -60,10 +60,12 @@ const Demo = () => {
|
||||
Current state: <span>{state}</span>
|
||||
</div>
|
||||
<div style={{ marginTop: 8 }}>
|
||||
<button onClick={handleBackClick} disabled={!history.position}>
|
||||
<button onClick={handleBackClick} disabled={!window.history.position}>
|
||||
< Back
|
||||
</button>
|
||||
<button onClick={handleForwardClick} disabled={history.position >= history.history.length - 1}>
|
||||
<button
|
||||
onClick={handleForwardClick}
|
||||
disabled={window.history.position >= window.history.window.history.length - 1}>
|
||||
Forward >
|
||||
</button>
|
||||
Step size:
|
||||
@ -74,7 +76,9 @@ const Demo = () => {
|
||||
<div>Current history</div>
|
||||
<div
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: JSON.stringify(history.history, null, 2).replace(/\n/g, '<br/>').replace(/ /g, ' '),
|
||||
__html: JSON.stringify(window.history.history, null, 2)
|
||||
.replace(/\n/g, '<br/>')
|
||||
.replace(/ /g, ' '),
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
@ -83,5 +87,5 @@ const Demo = () => {
|
||||
};
|
||||
|
||||
storiesOf('State/useStateWithHistory', module)
|
||||
.add('Docs', () => <ShowDocs md={require('../docs/useStateWithHistory.md')} />)
|
||||
.add('Docs', () => <ShowDocs md={require('../docs/useStateWithwindow.history.md')} />)
|
||||
.add('Demo', () => <Demo />);
|
||||
|
||||
@ -27,9 +27,14 @@ const Demo = () => {
|
||||
return (
|
||||
<div>
|
||||
<div>{readyState !== null ? 'Function will be called in 5 seconds' : 'Timer cancelled'}</div>
|
||||
<button onClick={cancelButtonClick}> {readyState === false ? 'cancel' : 'restart'} timeout</button>
|
||||
<button onClick={cancelButtonClick}>
|
||||
{' '}
|
||||
{readyState === false ? 'cancel' : 'restart'} timeout
|
||||
</button>
|
||||
<br />
|
||||
<div>Function state: {readyState === false ? 'Pending' : readyState ? 'Called' : 'Cancelled'}</div>
|
||||
<div>
|
||||
Function state: {readyState === false ? 'Pending' : readyState ? 'Called' : 'Cancelled'}
|
||||
</div>
|
||||
<div>{state}</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -8,8 +8,8 @@ const Demo = () => {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<code>useUnmount()</code> hook can be used to perform side-effects when component unmounts. This component will
|
||||
alert you when it is un-mounted.
|
||||
<code>useUnmount()</code> hook can be used to perform side-effects when component unmounts.
|
||||
This component will alert you when it is un-mounted.
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@ -27,7 +27,9 @@ const Demo = () => {
|
||||
<button onClick={() => remove(index)}>Remove</button>
|
||||
</div>
|
||||
))}
|
||||
<button onClick={() => upsert({ id: (list.length + 1).toString(), text: '' })}>Add item</button>
|
||||
<button onClick={() => upsert({ id: (list.length + 1).toString(), text: '' })}>
|
||||
Add item
|
||||
</button>
|
||||
<button onClick={() => set([])}>Reset</button>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -11,15 +11,13 @@ const Demo = () => {
|
||||
style={{
|
||||
width: '200vw',
|
||||
height: '200vh',
|
||||
}}
|
||||
>
|
||||
}}>
|
||||
<div
|
||||
style={{
|
||||
position: 'fixed',
|
||||
left: 0,
|
||||
right: 0,
|
||||
}}
|
||||
>
|
||||
}}>
|
||||
<div>x: {x}</div>
|
||||
<div>y: {y}</div>
|
||||
</div>
|
||||
|
||||
@ -8,8 +8,7 @@ export const CenterStory = ({ children }) => (
|
||||
alignItems: 'center',
|
||||
maxWidth: '400px',
|
||||
margin: '40px auto',
|
||||
}}
|
||||
>
|
||||
}}>
|
||||
<div style={{ width: '100%' }}>{children}</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -2,7 +2,7 @@ import * as React from 'react';
|
||||
|
||||
const h = React.createElement;
|
||||
|
||||
const ShowDocs = props => {
|
||||
const ShowDocs = (props) => {
|
||||
return h(
|
||||
'div',
|
||||
{},
|
||||
|
||||
@ -5,7 +5,7 @@ const useBreakpointA = createBreakpoint();
|
||||
const useBreakpointB = createBreakpoint({ mobileM: 350, laptop: 1024, tablet: 768 });
|
||||
|
||||
const originalInnerWidth = window.innerWidth;
|
||||
const changeInnerWidth = value =>
|
||||
const changeInnerWidth = (value) =>
|
||||
Object.defineProperty(window, 'innerWidth', { writable: true, configurable: true, value });
|
||||
const revert = () => changeInnerWidth(originalInnerWidth);
|
||||
|
||||
|
||||
@ -16,10 +16,13 @@ describe('when using created memo hook', () => {
|
||||
useMemoGetDouble = createMemo(getDouble);
|
||||
});
|
||||
|
||||
it.each([[1], [3], [5]])('should return same result as original function for argument %d', (val: number) => {
|
||||
const { result } = renderHook(() => useMemoGetDouble(val));
|
||||
expect(result.current).toBe(getDouble(val));
|
||||
});
|
||||
it.each([[1], [3], [5]])(
|
||||
'should return same result as original function for argument %d',
|
||||
(val: number) => {
|
||||
const { result } = renderHook(() => useMemoGetDouble(val));
|
||||
expect(result.current).toBe(getDouble(val));
|
||||
}
|
||||
);
|
||||
|
||||
it('should NOT call original function for same arguments', () => {
|
||||
let initialValue = 5;
|
||||
|
||||
@ -34,7 +34,7 @@ describe('when using created reducer hook', () => {
|
||||
|
||||
// Action creator to increment count, wait a second and then reset
|
||||
const addAndReset = () => {
|
||||
return dispatch => {
|
||||
return (dispatch) => {
|
||||
dispatch({ type: 'increment' });
|
||||
|
||||
setTimeout(() => {
|
||||
|
||||
@ -26,12 +26,16 @@ describe('when using created hook', () => {
|
||||
it('should throw out of a provider', () => {
|
||||
const [useSharedNumber] = createReducerContext(reducer, 0);
|
||||
const { result } = renderHook(() => useSharedNumber());
|
||||
expect(result.error).toEqual(new Error('useReducerContext must be used inside a ReducerProvider.'));
|
||||
expect(result.error).toEqual(
|
||||
new Error('useReducerContext must be used inside a ReducerProvider.')
|
||||
);
|
||||
});
|
||||
|
||||
const setUp = () => {
|
||||
const [useSharedNumber, SharedNumberProvider] = createReducerContext(reducer, 0);
|
||||
const wrapper: React.FC = ({ children }) => <SharedNumberProvider>{children}</SharedNumberProvider>;
|
||||
const wrapper: React.FC = ({ children }) => (
|
||||
<SharedNumberProvider>{children}</SharedNumberProvider>
|
||||
);
|
||||
return renderHook(() => useSharedNumber(), { wrapper });
|
||||
};
|
||||
|
||||
@ -81,11 +85,15 @@ describe('when using among multiple components', () => {
|
||||
</SharedNumberProvider>
|
||||
);
|
||||
|
||||
expect(baseElement.innerHTML).toBe('<div><p>0</p><p>0</p><button type="button">INCREMENT</button></div>');
|
||||
expect(baseElement.innerHTML).toBe(
|
||||
'<div><p>0</p><p>0</p><button type="button">INCREMENT</button></div>'
|
||||
);
|
||||
|
||||
fireEvent.click(getByText('INCREMENT'));
|
||||
|
||||
expect(baseElement.innerHTML).toBe('<div><p>1</p><p>1</p><button type="button">INCREMENT</button></div>');
|
||||
expect(baseElement.innerHTML).toBe(
|
||||
'<div><p>1</p><p>1</p><button type="button">INCREMENT</button></div>'
|
||||
);
|
||||
});
|
||||
|
||||
it('should be in update independently when under different providers', () => {
|
||||
@ -101,11 +109,15 @@ describe('when using among multiple components', () => {
|
||||
</>
|
||||
);
|
||||
|
||||
expect(baseElement.innerHTML).toBe('<div><p>0</p><p>0</p><button type="button">INCREMENT</button></div>');
|
||||
expect(baseElement.innerHTML).toBe(
|
||||
'<div><p>0</p><p>0</p><button type="button">INCREMENT</button></div>'
|
||||
);
|
||||
|
||||
fireEvent.click(getByText('INCREMENT'));
|
||||
|
||||
expect(baseElement.innerHTML).toBe('<div><p>0</p><p>1</p><button type="button">INCREMENT</button></div>');
|
||||
expect(baseElement.innerHTML).toBe(
|
||||
'<div><p>0</p><p>1</p><button type="button">INCREMENT</button></div>'
|
||||
);
|
||||
});
|
||||
|
||||
it('should not update component that do not use the state context', () => {
|
||||
@ -125,11 +137,15 @@ describe('when using among multiple components', () => {
|
||||
</>
|
||||
);
|
||||
|
||||
expect(baseElement.innerHTML).toBe('<div><p>static</p><p>0</p><button type="button">INCREMENT</button></div>');
|
||||
expect(baseElement.innerHTML).toBe(
|
||||
'<div><p>static</p><p>0</p><button type="button">INCREMENT</button></div>'
|
||||
);
|
||||
|
||||
fireEvent.click(getByText('INCREMENT'));
|
||||
|
||||
expect(baseElement.innerHTML).toBe('<div><p>static</p><p>1</p><button type="button">INCREMENT</button></div>');
|
||||
expect(baseElement.innerHTML).toBe(
|
||||
'<div><p>static</p><p>1</p><button type="button">INCREMENT</button></div>'
|
||||
);
|
||||
|
||||
expect(renderCount).toBe(1);
|
||||
});
|
||||
|
||||
@ -68,11 +68,15 @@ describe('when using among multiple components', () => {
|
||||
</SharedTextProvider>
|
||||
);
|
||||
|
||||
expect(baseElement.innerHTML).toBe('<div><p>init</p><p>init</p><button type="button">UPDATE</button></div>');
|
||||
expect(baseElement.innerHTML).toBe(
|
||||
'<div><p>init</p><p>init</p><button type="button">UPDATE</button></div>'
|
||||
);
|
||||
|
||||
fireEvent.click(getByText('UPDATE'));
|
||||
|
||||
expect(baseElement.innerHTML).toBe('<div><p>changed</p><p>changed</p><button type="button">UPDATE</button></div>');
|
||||
expect(baseElement.innerHTML).toBe(
|
||||
'<div><p>changed</p><p>changed</p><button type="button">UPDATE</button></div>'
|
||||
);
|
||||
});
|
||||
|
||||
it('should be in update independently when under different providers', () => {
|
||||
@ -88,11 +92,15 @@ describe('when using among multiple components', () => {
|
||||
</>
|
||||
);
|
||||
|
||||
expect(baseElement.innerHTML).toBe('<div><p>init</p><p>init</p><button type="button">UPDATE</button></div>');
|
||||
expect(baseElement.innerHTML).toBe(
|
||||
'<div><p>init</p><p>init</p><button type="button">UPDATE</button></div>'
|
||||
);
|
||||
|
||||
fireEvent.click(getByText('UPDATE'));
|
||||
|
||||
expect(baseElement.innerHTML).toBe('<div><p>init</p><p>changed</p><button type="button">UPDATE</button></div>');
|
||||
expect(baseElement.innerHTML).toBe(
|
||||
'<div><p>init</p><p>changed</p><button type="button">UPDATE</button></div>'
|
||||
);
|
||||
});
|
||||
|
||||
it('should not update component that do not use the state context', () => {
|
||||
@ -112,11 +120,15 @@ describe('when using among multiple components', () => {
|
||||
</>
|
||||
);
|
||||
|
||||
expect(baseElement.innerHTML).toBe('<div><p>static</p><p>init</p><button type="button">UPDATE</button></div>');
|
||||
expect(baseElement.innerHTML).toBe(
|
||||
'<div><p>static</p><p>init</p><button type="button">UPDATE</button></div>'
|
||||
);
|
||||
|
||||
fireEvent.click(getByText('UPDATE'));
|
||||
|
||||
expect(baseElement.innerHTML).toBe('<div><p>static</p><p>changed</p><button type="button">UPDATE</button></div>');
|
||||
expect(baseElement.innerHTML).toBe(
|
||||
'<div><p>static</p><p>changed</p><button type="button">UPDATE</button></div>'
|
||||
);
|
||||
|
||||
expect(renderCount).toBe(1);
|
||||
});
|
||||
|
||||
@ -25,8 +25,7 @@ describe('resolveHookState', () => {
|
||||
});
|
||||
|
||||
it('should not pass 2nd parameter to function if it not awaited', () => {
|
||||
const spy = jest.fn(() => {
|
||||
});
|
||||
const spy = jest.fn(() => {});
|
||||
/* @ts-expect-error */
|
||||
resolveHookState(spy, 123);
|
||||
expect(spy).toHaveBeenCalled();
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
|
||||
import { renderHook } from '@testing-library/react-hooks';
|
||||
import { useCallback } from 'react';
|
||||
import useAsync from '../src/useAsync';
|
||||
@ -13,7 +12,7 @@ describe('useAsync', () => {
|
||||
let callCount = 0;
|
||||
|
||||
const resolver = async () => {
|
||||
return new Promise(resolve => {
|
||||
return new Promise((resolve) => {
|
||||
callCount++;
|
||||
|
||||
const wait = setTimeout(() => {
|
||||
@ -107,7 +106,7 @@ describe('useAsync', () => {
|
||||
return 'new value';
|
||||
};
|
||||
|
||||
beforeEach(done => {
|
||||
beforeEach((done) => {
|
||||
callCount = 0;
|
||||
|
||||
hook = renderHook(({ fn }) => useAsync(fn, [fn]), {
|
||||
@ -138,12 +137,12 @@ describe('useAsync', () => {
|
||||
let callCount = 0;
|
||||
let hook;
|
||||
|
||||
const staticFunction = async counter => {
|
||||
const staticFunction = async (counter) => {
|
||||
callCount++;
|
||||
return `counter is ${counter} and callCount is ${callCount}`;
|
||||
};
|
||||
|
||||
beforeEach(done => {
|
||||
beforeEach((done) => {
|
||||
callCount = 0;
|
||||
hook = renderHook(
|
||||
({ fn, counter }) => {
|
||||
|
||||
@ -23,9 +23,12 @@ describe('useAsyncFn', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
// NOTE: renderHook isn't good at inferring array types
|
||||
hook = renderHook<{ fn: AdderFn }, [AsyncState<number>, AdderFn]>(({ fn }) => useAsyncFn(fn), {
|
||||
initialProps: { fn: adder },
|
||||
});
|
||||
hook = renderHook<{ fn: AdderFn }, [AsyncState<number>, AdderFn]>(
|
||||
({ fn }) => useAsyncFn(fn),
|
||||
{
|
||||
initialProps: { fn: adder },
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('awaits the result', async () => {
|
||||
@ -57,11 +60,14 @@ describe('useAsyncFn', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
// NOTE: renderHook isn't good at inferring array types
|
||||
hook = renderHook<{ fn: AdderFn }, [AsyncState<number>, AdderFn]>(({ fn }) => useAsyncFn(fn), {
|
||||
initialProps: {
|
||||
fn: adder,
|
||||
},
|
||||
});
|
||||
hook = renderHook<{ fn: AdderFn }, [AsyncState<number>, AdderFn]>(
|
||||
({ fn }) => useAsyncFn(fn),
|
||||
{
|
||||
initialProps: {
|
||||
fn: adder,
|
||||
},
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('initially does not have a value', () => {
|
||||
@ -98,18 +104,22 @@ describe('useAsyncFn', () => {
|
||||
it('should only consider last call and discard previous ones', async () => {
|
||||
const queuedPromises: { id: number; resolve: () => void }[] = [];
|
||||
const delayedFunction1 = () => {
|
||||
return new Promise<number>(resolve => queuedPromises.push({ id: 1, resolve: () => resolve(1) }));
|
||||
return new Promise<number>((resolve) =>
|
||||
queuedPromises.push({ id: 1, resolve: () => resolve(1) })
|
||||
);
|
||||
};
|
||||
const delayedFunction2 = () => {
|
||||
return new Promise<number>(resolve => queuedPromises.push({ id: 2, resolve: () => resolve(2) }));
|
||||
return new Promise<number>((resolve) =>
|
||||
queuedPromises.push({ id: 2, resolve: () => resolve(2) })
|
||||
);
|
||||
};
|
||||
|
||||
const hook = renderHook<{ fn: () => Promise<number> }, [AsyncState<number>, () => Promise<number>]>(
|
||||
({ fn }) => useAsyncFn(fn, [fn]),
|
||||
{
|
||||
initialProps: { fn: delayedFunction1 },
|
||||
}
|
||||
);
|
||||
const hook = renderHook<
|
||||
{ fn: () => Promise<number> },
|
||||
[AsyncState<number>, () => Promise<number>]
|
||||
>(({ fn }) => useAsyncFn(fn, [fn]), {
|
||||
initialProps: { fn: delayedFunction1 },
|
||||
});
|
||||
act(() => {
|
||||
hook.result.current[1](); // invoke 1st callback
|
||||
});
|
||||
@ -131,12 +141,12 @@ describe('useAsyncFn', () => {
|
||||
const fetch = async () => 'new state';
|
||||
const initialState = { loading: false, value: 'init state' };
|
||||
|
||||
const hook = renderHook<{ fn: () => Promise<string> }, [AsyncState<string>, () => Promise<string>]>(
|
||||
({ fn }) => useAsyncFn(fn, [fn], initialState),
|
||||
{
|
||||
initialProps: { fn: fetch },
|
||||
}
|
||||
);
|
||||
const hook = renderHook<
|
||||
{ fn: () => Promise<string> },
|
||||
[AsyncState<string>, () => Promise<string>]
|
||||
>(({ fn }) => useAsyncFn(fn, [fn], initialState), {
|
||||
initialProps: { fn: fetch },
|
||||
});
|
||||
|
||||
const [state, callback] = hook.result.current;
|
||||
expect(state.loading).toBe(false);
|
||||
|
||||
@ -4,7 +4,7 @@ import { useCopyToClipboard } from '../src';
|
||||
|
||||
const valueToRaiseMockException = 'fake input causing exception in copy to clipboard';
|
||||
jest.mock('copy-to-clipboard', () =>
|
||||
jest.fn().mockImplementation(input => {
|
||||
jest.fn().mockImplementation((input) => {
|
||||
if (input === valueToRaiseMockException) {
|
||||
throw new Error(input);
|
||||
}
|
||||
@ -14,15 +14,12 @@ jest.mock('copy-to-clipboard', () =>
|
||||
|
||||
describe('useCopyToClipboard', () => {
|
||||
let hook;
|
||||
let consoleErrorSpy = jest.spyOn(global.console, 'error').mockImplementation(() => {
|
||||
});
|
||||
|
||||
let consoleErrorSpy = jest.spyOn(global.console, 'error').mockImplementation(() => {});
|
||||
|
||||
beforeEach(() => {
|
||||
hook = renderHook(() => useCopyToClipboard());
|
||||
});
|
||||
|
||||
|
||||
afterAll(() => {
|
||||
consoleErrorSpy.mockRestore();
|
||||
jest.unmock('copy-to-clipboard');
|
||||
|
||||
@ -187,15 +187,21 @@ describe('should `console.error` on unexpected inputs', () => {
|
||||
|
||||
// @ts-ignore
|
||||
act(() => inc(false));
|
||||
expect(spy.mock.calls[0][0]).toBe('delta has to be a number or function returning a number, got boolean');
|
||||
expect(spy.mock.calls[0][0]).toBe(
|
||||
'delta has to be a number or function returning a number, got boolean'
|
||||
);
|
||||
|
||||
// @ts-ignore
|
||||
act(() => dec(false));
|
||||
expect(spy.mock.calls[1][0]).toBe('delta has to be a number or function returning a number, got boolean');
|
||||
expect(spy.mock.calls[1][0]).toBe(
|
||||
'delta has to be a number or function returning a number, got boolean'
|
||||
);
|
||||
|
||||
// @ts-ignore
|
||||
act(() => reset({}));
|
||||
expect(spy.mock.calls[2][0]).toBe('value has to be a number or function returning a number, got object');
|
||||
expect(spy.mock.calls[2][0]).toBe(
|
||||
'value has to be a number or function returning a number, got object'
|
||||
);
|
||||
|
||||
spy.mockRestore();
|
||||
});
|
||||
|
||||
@ -11,7 +11,9 @@ const mockEffectCallback = jest.fn().mockReturnValue(mockEffectCleanup);
|
||||
|
||||
it('should run provided object once', () => {
|
||||
const { rerender: rerenderNormal } = renderHook(() => useEffect(mockEffectNormal, [options]));
|
||||
const { rerender: rerenderDeep } = renderHook(() => useCustomCompareEffect(mockEffectDeep, [options], isDeepEqual));
|
||||
const { rerender: rerenderDeep } = renderHook(() =>
|
||||
useCustomCompareEffect(mockEffectDeep, [options], isDeepEqual)
|
||||
);
|
||||
|
||||
expect(mockEffectNormal).toHaveBeenCalledTimes(1);
|
||||
expect(mockEffectDeep).toHaveBeenCalledTimes(1);
|
||||
@ -32,7 +34,9 @@ it('should run provided object once', () => {
|
||||
});
|
||||
|
||||
it('should run clean-up provided on unmount', () => {
|
||||
const { unmount } = renderHook(() => useCustomCompareEffect(mockEffectCallback, [options], isDeepEqual));
|
||||
const { unmount } = renderHook(() =>
|
||||
useCustomCompareEffect(mockEffectCallback, [options], isDeepEqual)
|
||||
);
|
||||
expect(mockEffectCleanup).not.toHaveBeenCalled();
|
||||
|
||||
unmount();
|
||||
|
||||
@ -10,7 +10,9 @@ const mockEffectCallback = jest.fn().mockReturnValue(mockEffectCleanup);
|
||||
|
||||
it('should run provided object once', () => {
|
||||
const { rerender: rerenderNormal } = renderHook(() => useEffect(mockEffectNormal, [options]));
|
||||
const { rerender: rerenderDeep } = renderHook(() => useDeepCompareEffect(mockEffectDeep, [options]));
|
||||
const { rerender: rerenderDeep } = renderHook(() =>
|
||||
useDeepCompareEffect(mockEffectDeep, [options])
|
||||
);
|
||||
|
||||
expect(mockEffectNormal).toHaveBeenCalledTimes(1);
|
||||
expect(mockEffectDeep).toHaveBeenCalledTimes(1);
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
import { act, renderHook } from '@testing-library/react-hooks';
|
||||
import useDefault from '../src/useDefault';
|
||||
|
||||
const setUp = (defaultValue: any, initialValue: any) => renderHook(() => useDefault(defaultValue, initialValue));
|
||||
const setUp = (defaultValue: any, initialValue: any) =>
|
||||
renderHook(() => useDefault(defaultValue, initialValue));
|
||||
|
||||
describe.each`
|
||||
valueType | defaultValue | initialValue | anotherValue
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user