mirror of
https://github.com/streamich/react-use.git
synced 2026-01-25 14:17:16 +00:00
updated formating and fixed errors
This commit is contained in:
parent
3ce080cdf3
commit
db07ab65bf
@ -151,7 +151,7 @@
|
||||
"lint-staged": {
|
||||
"src/**/**/*.{ts,tsx}": [
|
||||
"eslint --fix",
|
||||
"prettier --write",
|
||||
"yarn prettier --write",
|
||||
"git add"
|
||||
]
|
||||
},
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
/* eslint-disable */
|
||||
import { useMemo, useState } from 'react';
|
||||
import useMountedState from './useMountedState';
|
||||
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
/* eslint-disable */
|
||||
import { useEffect } from 'react';
|
||||
import { isClient } from './util';
|
||||
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
/* eslint-disable */
|
||||
import { RefObject, useState } from 'react';
|
||||
import screenfull from 'screenfull';
|
||||
import useIsomorphicLayoutEffect from './useIsomorphicLayoutEffect';
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
/* eslint-disable */
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
export interface GeoLocationSensorState {
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
/* eslint-disable */
|
||||
import { Dispatch, useMemo, useRef } from 'react';
|
||||
import useUpdate from './useUpdate';
|
||||
import { HookState, InitialHookState, resolveHookState } from './util/resolveHookState';
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
/* eslint-disable */
|
||||
import { useCallback, useRef } from 'react';
|
||||
import useUpdate from './useUpdate';
|
||||
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
/* eslint-disable */
|
||||
import { useEffect, useState } from 'react';
|
||||
import { throttle } from 'throttle-debounce';
|
||||
import { off, on } from './util';
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
/* eslint-disable */
|
||||
import { RefObject, useEffect, useState } from 'react';
|
||||
|
||||
const useIntersection = (
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
/* eslint-disable */
|
||||
import { DependencyList, useMemo } from 'react';
|
||||
import useEvent, { UseEventTarget } from './useEvent';
|
||||
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
/* eslint-disable */
|
||||
import { useEffect } from 'react';
|
||||
|
||||
const useLifecycles = (mount, unmount?) => {
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
/* eslint-disable */
|
||||
import { useMemo, useRef } from 'react';
|
||||
import useUpdate from './useUpdate';
|
||||
import { InitialHookState, ResolvableHookState, resolveHookState } from './util/resolveHookState';
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
/* eslint-disable */
|
||||
import { useState, useCallback, Dispatch, SetStateAction } from 'react';
|
||||
import { isClient } from './util';
|
||||
|
||||
@ -28,6 +27,7 @@ const useLocalStorage = <T>(
|
||||
|
||||
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>(() => {
|
||||
try {
|
||||
const serializer = options ? (options.raw ? String : options.serializer) : JSON.stringify;
|
||||
@ -47,6 +47,7 @@ const useLocalStorage = <T>(
|
||||
}
|
||||
});
|
||||
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
const set: Dispatch<SetStateAction<T | undefined>> = useCallback(
|
||||
(valOrFunc) => {
|
||||
try {
|
||||
@ -72,6 +73,7 @@ const useLocalStorage = <T>(
|
||||
[key, setState]
|
||||
);
|
||||
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
const remove = useCallback(() => {
|
||||
try {
|
||||
localStorage.removeItem(key);
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
/* eslint-disable */
|
||||
import { useEffect, useState } from 'react';
|
||||
import { isClient, off, on } from './util';
|
||||
|
||||
const history = window.history;
|
||||
const location = window.location;
|
||||
|
||||
const patchHistoryMethod = (method) => {
|
||||
const original = history[method];
|
||||
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
/* eslint-disable */
|
||||
import { RefObject, useEffect, useRef } from 'react';
|
||||
|
||||
export function getClosestBody(el: Element | HTMLElement | HTMLIFrameElement | null): HTMLElement | null {
|
||||
@ -46,7 +45,8 @@ let documentListenerAdded = false;
|
||||
export default !doc
|
||||
? function useLockBodyMock(_locked: boolean = true, _elementRef?: RefObject<HTMLElement>) {}
|
||||
: function useLockBody(locked: boolean = true, elementRef?: RefObject<HTMLElement>) {
|
||||
elementRef = elementRef || useRef(doc!.body);
|
||||
const bodyRef = useRef(doc!.body);
|
||||
elementRef = elementRef || bodyRef;
|
||||
|
||||
const lock = (body) => {
|
||||
const bodyInfo = bodies.get(body);
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
/* eslint-disable */
|
||||
import { useCallback, useRef } from 'react';
|
||||
|
||||
interface Options {
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
/* eslint-disable */
|
||||
import { useState, useMemo, useCallback } from 'react';
|
||||
|
||||
export interface StableActions<T extends object> {
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
/* eslint-disable */
|
||||
import { useState, useEffect, useRef, RefObject } from 'react';
|
||||
import ResizeObserver from 'resize-observer-polyfill';
|
||||
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
/* eslint-disable */
|
||||
import { Dispatch, SetStateAction, useCallback, useRef, useState } from 'react';
|
||||
|
||||
export interface StateMediator<S = any> {
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
/* eslint-disable */
|
||||
import { RefObject, useEffect } from 'react';
|
||||
|
||||
import useRafState from './useRafState';
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
/* eslint-disable */
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { StateValidator, UseStateValidatorReturn, ValidityState } from './useStateValidator';
|
||||
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
/* eslint-disable */
|
||||
import { useEffect, useState } from 'react';
|
||||
import { off, on } from './util';
|
||||
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
/* eslint-disable */
|
||||
import { useEffect, useState } from 'react';
|
||||
import { off, on } from './util';
|
||||
|
||||
@ -12,6 +11,8 @@ const defaultState: OrientationState = {
|
||||
type: 'landscape-primary',
|
||||
};
|
||||
|
||||
const screen = window.screen;
|
||||
|
||||
const useOrientation = (initialState: OrientationState = defaultState) => {
|
||||
const [state, setState] = useState(initialState);
|
||||
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
/* eslint-disable */
|
||||
import { useEffect } from 'react';
|
||||
|
||||
const usePageLeave = (onPageLeave, args = []) => {
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
/* eslint-disable */
|
||||
import { useEffect, useState } from 'react';
|
||||
import { off, on } from './util';
|
||||
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
/* eslint-disable */
|
||||
import { useCallback } from 'react';
|
||||
import useMountedState from './useMountedState';
|
||||
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
/* eslint-disable */
|
||||
import { RefObject, useEffect } from 'react';
|
||||
|
||||
import useRafState from './useRafState';
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
/* eslint-disable */
|
||||
import { scrollbarWidth } from '@xobotyi/scrollbar-width';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
/* eslint-disable */
|
||||
import { RefObject, useEffect, useState } from 'react';
|
||||
|
||||
const useScrolling = (ref: RefObject<HTMLElement>): boolean => {
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
/* eslint-disable */
|
||||
import { useState, useEffect } from 'react';
|
||||
|
||||
const getValue = (search: string, param: string) => new URLSearchParams(search).get(param);
|
||||
|
||||
export type UseQueryParam = (param: string) => string | null;
|
||||
|
||||
const location = window.location;
|
||||
|
||||
const useSearchParam: UseQueryParam = (param) => {
|
||||
const [value, setValue] = useState<string | null>(() => getValue(location.search, param));
|
||||
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
/* eslint-disable */
|
||||
import { useEffect, useState } from 'react';
|
||||
import { isClient } from './util';
|
||||
|
||||
@ -7,6 +6,7 @@ const useSessionStorage = <T>(key: string, initialValue?: T, raw?: boolean): [T,
|
||||
return [initialValue as T, () => {}];
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
const [state, setState] = useState<T>(() => {
|
||||
try {
|
||||
const sessionStorageValue = sessionStorage.getItem(key);
|
||||
@ -24,6 +24,7 @@ const useSessionStorage = <T>(key: string, initialValue?: T, raw?: boolean): [T,
|
||||
}
|
||||
});
|
||||
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
useEffect(() => {
|
||||
try {
|
||||
const serializedState = raw ? String(state) : JSON.stringify(state);
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
/* eslint-disable */
|
||||
import { useState, useMemo, useCallback } from 'react';
|
||||
|
||||
export interface StableActions<K> {
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
/* eslint-disable */
|
||||
import * as React from 'react';
|
||||
import { isClient } from './util';
|
||||
|
||||
@ -21,6 +20,7 @@ const useSize = (
|
||||
return [typeof element === 'function' ? element({ width, height }) : element, { width, height }];
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
const [state, setState] = useState<State>({ width, height });
|
||||
|
||||
if (typeof element === 'function') {
|
||||
@ -28,6 +28,7 @@ const useSize = (
|
||||
}
|
||||
|
||||
const style = element.props.style || {};
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
const ref = useRef<HTMLIFrameElement | null>(null);
|
||||
let window: Window | null = null;
|
||||
const setSize = () => {
|
||||
@ -46,6 +47,7 @@ const useSize = (
|
||||
DRAF(setSize);
|
||||
};
|
||||
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
useEffect(() => {
|
||||
const iframe: HTMLIFrameElement | null = ref.current;
|
||||
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
/* eslint-disable */
|
||||
import { useEffect, useRef, RefObject, CSSProperties } from 'react';
|
||||
|
||||
import { isClient, off, on } from './util';
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
/* eslint-disable */
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { Spring, SpringSystem } from 'rebound';
|
||||
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
/* eslint-disable */
|
||||
import useIsomorphicLayoutEffect from './useIsomorphicLayoutEffect';
|
||||
|
||||
const isFocusedElementEditable = () => {
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
/* eslint-disable */
|
||||
import { useMemo, useRef } from 'react';
|
||||
import useMountedState from './useMountedState';
|
||||
import useUpdate from './useUpdate';
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
/* eslint-disable */
|
||||
import { Dispatch, SetStateAction, useCallback, useEffect, useRef, useState } from 'react';
|
||||
|
||||
export type ValidityState = [boolean | undefined, ...any[]];
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
/* eslint-disable */
|
||||
import { Dispatch, useCallback, useMemo, useRef, useState } from 'react';
|
||||
import { useFirstMountState } from './useFirstMountState';
|
||||
import { InitialHookState, ResolvableHookState, resolveHookState } from './util/resolveHookState';
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
/* eslint-disable */
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import useUnmount from './useUnmount';
|
||||
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
/* eslint-disable */
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import useUnmount from './useUnmount';
|
||||
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
/* eslint-disable */
|
||||
import { useCallback, useEffect, useRef } from 'react';
|
||||
|
||||
export type UseTimeoutFnReturn = [() => boolean | null, () => void, () => void];
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
/* eslint-disable */
|
||||
import { useRef, useEffect } from 'react';
|
||||
export interface UseTitleOptions {
|
||||
restoreOnUnmount?: boolean;
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
/* eslint-disable */
|
||||
import { useEffect } from 'react';
|
||||
import { useFirstMountState } from './useFirstMountState';
|
||||
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
/* eslint-disable */
|
||||
import { useEffect } from 'react';
|
||||
|
||||
export type VibrationPattern = number | number[];
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
/* eslint-disable */
|
||||
import { useEffect } from 'react';
|
||||
import { isClient } from './util';
|
||||
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
/* eslint-disable */
|
||||
import { useEffect } from 'react';
|
||||
|
||||
import useRafState from './useRafState';
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
/* eslint-disable */
|
||||
import * as React from 'react';
|
||||
import { useEffect, useRef } from 'react';
|
||||
import useSetState from '../useSetState';
|
||||
@ -26,210 +25,213 @@ export interface HTMLMediaControls {
|
||||
seek: (time: number) => void;
|
||||
}
|
||||
|
||||
const createHTMLMediaHook = (tag: 'audio' | 'video') => {
|
||||
const hook = (
|
||||
elOrProps: HTMLMediaProps | React.ReactElement<HTMLMediaProps>
|
||||
): [React.ReactElement<HTMLMediaProps>, HTMLMediaState, HTMLMediaControls, { current: HTMLAudioElement | null }] => {
|
||||
let element: React.ReactElement<any> | undefined;
|
||||
let props: HTMLMediaProps;
|
||||
type createHTMLMediaHookReturn = [
|
||||
React.ReactElement<HTMLMediaProps>,
|
||||
HTMLMediaState,
|
||||
HTMLMediaControls,
|
||||
{ current: HTMLAudioElement | null }
|
||||
];
|
||||
|
||||
if (React.isValidElement(elOrProps)) {
|
||||
element = elOrProps;
|
||||
props = element.props;
|
||||
} else {
|
||||
props = elOrProps as HTMLMediaProps;
|
||||
}
|
||||
const createHTMLMediaHook = (tag: 'audio' | 'video') => (
|
||||
elOrProps: HTMLMediaProps | React.ReactElement<HTMLMediaProps>
|
||||
): createHTMLMediaHookReturn => {
|
||||
let element: React.ReactElement<any> | undefined;
|
||||
let props: HTMLMediaProps;
|
||||
|
||||
const [state, setState] = useSetState<HTMLMediaState>({
|
||||
buffered: [],
|
||||
time: 0,
|
||||
duration: 0,
|
||||
paused: true,
|
||||
muted: false,
|
||||
volume: 1,
|
||||
});
|
||||
const ref = useRef<HTMLAudioElement | null>(null);
|
||||
if (React.isValidElement(elOrProps)) {
|
||||
element = elOrProps;
|
||||
props = element.props;
|
||||
} else {
|
||||
props = elOrProps as HTMLMediaProps;
|
||||
}
|
||||
|
||||
const wrapEvent = (userEvent, proxyEvent?) => {
|
||||
return (event) => {
|
||||
try {
|
||||
proxyEvent && proxyEvent(event);
|
||||
} finally {
|
||||
userEvent && userEvent(event);
|
||||
}
|
||||
};
|
||||
};
|
||||
const [state, setState] = useSetState<HTMLMediaState>({
|
||||
buffered: [],
|
||||
time: 0,
|
||||
duration: 0,
|
||||
paused: true,
|
||||
muted: false,
|
||||
volume: 1,
|
||||
});
|
||||
const ref = useRef<HTMLAudioElement | null>(null);
|
||||
|
||||
const onPlay = () => setState({ paused: false });
|
||||
const onPause = () => setState({ paused: true });
|
||||
const onVolumeChange = () => {
|
||||
const el = ref.current;
|
||||
if (!el) {
|
||||
return;
|
||||
const wrapEvent = (userEvent, proxyEvent?) => {
|
||||
return (event) => {
|
||||
try {
|
||||
proxyEvent && proxyEvent(event);
|
||||
} finally {
|
||||
userEvent && userEvent(event);
|
||||
}
|
||||
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];
|
||||
};
|
||||
|
||||
return hook;
|
||||
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,4 +1,4 @@
|
||||
/* eslint-disable */
|
||||
|
||||
import { renderHook } from '@testing-library/react-hooks';
|
||||
import { useCallback } from 'react';
|
||||
import useAsync from '../src/useAsync';
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
/* eslint-disable */
|
||||
|
||||
import useLocalStorage from '../src/useLocalStorage';
|
||||
import 'jest-localstorage-mock';
|
||||
import { renderHook, act } from '@testing-library/react-hooks';
|
||||
@ -33,7 +33,7 @@ describe(useLocalStorage, () => {
|
||||
it('does not clobber existing localStorage with initialState', () => {
|
||||
localStorage.setItem('foo', '"bar"');
|
||||
const { result } = renderHook(() => useLocalStorage('foo', 'buzz'));
|
||||
result.current; // invoke current to make sure things are set
|
||||
expect(result.current).toBeTruthy();
|
||||
expect(localStorage.__STORE__.foo).toEqual('"bar"');
|
||||
});
|
||||
|
||||
@ -154,7 +154,7 @@ describe(useLocalStorage, () => {
|
||||
it('rejects nullish or undefined keys', () => {
|
||||
const { result } = renderHook(() => useLocalStorage(null as any));
|
||||
try {
|
||||
result.current;
|
||||
const current = result.current;
|
||||
fail('hook should have thrown');
|
||||
} catch (e) {
|
||||
expect(String(e)).toMatch(/key may not be/i);
|
||||
@ -166,7 +166,7 @@ describe(useLocalStorage, () => {
|
||||
it('memoizes an object between rerenders', () => {
|
||||
const { result, rerender } = renderHook(() => useLocalStorage('foo', { ok: true }));
|
||||
|
||||
result.current; // if localStorage isn't set then r1 and r2 will be different
|
||||
const current = result.current; // if localStorage isn't set then r1 and r2 will be different
|
||||
rerender();
|
||||
const [r2] = result.current;
|
||||
rerender();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user