-
-
Notifications
You must be signed in to change notification settings - Fork 591
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: import react-use hooks and remove it as a dep
- Loading branch information
1 parent
d721cc5
commit a43a40b
Showing
4 changed files
with
226 additions
and
198 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,213 @@ | ||
// Imported from https://github.com/streamich/react-use | ||
import { | ||
type Dispatch, | ||
type EffectCallback, | ||
type SetStateAction, | ||
useCallback, | ||
useEffect, | ||
useLayoutEffect, | ||
useRef, | ||
useState | ||
} from 'react' | ||
|
||
const noop = () => {} | ||
|
||
function on<T extends Window | Document | HTMLElement | EventTarget>( | ||
obj: T | null, | ||
...args: | ||
| Parameters<T['addEventListener']> | ||
| [string, ((...args: any[]) => any) | null, ...any] | ||
): void { | ||
if (obj && obj.addEventListener) { | ||
obj.addEventListener( | ||
...(args as Parameters<HTMLElement['addEventListener']>) | ||
) | ||
} | ||
} | ||
|
||
function off<T extends Window | Document | HTMLElement | EventTarget>( | ||
obj: T | null, | ||
...args: | ||
| Parameters<T['removeEventListener']> | ||
| [string, ((...args: any[]) => any) | null, ...any] | ||
): void { | ||
if (obj && obj.removeEventListener) { | ||
obj.removeEventListener( | ||
...(args as Parameters<HTMLElement['removeEventListener']>) | ||
) | ||
} | ||
} | ||
|
||
const isBrowser = typeof window !== 'undefined' | ||
|
||
export const useWindowSize = ( | ||
initialWidth = Infinity, | ||
initialHeight = Infinity | ||
) => { | ||
const [state, setState] = useRafState<{ width: number; height: number }>({ | ||
width: isBrowser ? window.innerWidth : initialWidth, | ||
height: isBrowser ? window.innerHeight : initialHeight | ||
}) | ||
|
||
useEffect((): (() => void) | void => { | ||
if (isBrowser) { | ||
const handler = () => { | ||
setState({ | ||
width: window.innerWidth, | ||
height: window.innerHeight | ||
}) | ||
} | ||
|
||
on(window, 'resize', handler) | ||
|
||
return () => { | ||
off(window, 'resize', handler) | ||
} | ||
} | ||
// eslint-disable-next-line react-hooks/exhaustive-deps | ||
}, []) | ||
|
||
return state | ||
} | ||
|
||
export const useEffectOnce = (effect: EffectCallback) => { | ||
// eslint-disable-next-line react-hooks/exhaustive-deps | ||
useEffect(effect, []) | ||
} | ||
|
||
export const useUnmount = (fn: () => any): void => { | ||
const fnRef = useRef(fn) | ||
|
||
// update the ref each render so if it change the newest callback will be invoked | ||
fnRef.current = fn | ||
|
||
// eslint-disable-next-line unicorn/consistent-function-scoping | ||
useEffectOnce(() => () => fnRef.current()) | ||
} | ||
|
||
export const useRafState = <S>( | ||
initialState: S | (() => S) | ||
): [S, Dispatch<SetStateAction<S>>] => { | ||
const frame = useRef(0) | ||
const [state, setState] = useState(initialState) | ||
|
||
const setRafState = useCallback((value: S | ((prevState: S) => S)) => { | ||
cancelAnimationFrame(frame.current) | ||
|
||
frame.current = requestAnimationFrame(() => { | ||
setState(value) | ||
}) | ||
}, []) | ||
|
||
useUnmount(() => { | ||
cancelAnimationFrame(frame.current) | ||
}) | ||
|
||
return [state, setRafState] | ||
} | ||
|
||
type parserOptions<T> = | ||
| { | ||
raw: true | ||
} | ||
| { | ||
raw: false | ||
serializer: (value: T) => string | ||
deserializer: (value: string) => T | ||
} | ||
|
||
export const useLocalStorage = <T>( | ||
key: string, | ||
initialValue?: T, | ||
options?: parserOptions<T> | ||
): [T | undefined, Dispatch<SetStateAction<T | undefined>>, () => void] => { | ||
if (!isBrowser) { | ||
return [initialValue as T, noop, noop] | ||
} | ||
if (!key) { | ||
throw new Error('useLocalStorage key may not be falsy') | ||
} | ||
|
||
const deserializer = options | ||
? options.raw | ||
? (value: any) => value | ||
: options.deserializer | ||
: JSON.parse | ||
|
||
// eslint-disable-next-line react-hooks/rules-of-hooks | ||
const initializer = useRef((key: string) => { | ||
try { | ||
const serializer = options | ||
? options.raw | ||
? String | ||
: options.serializer | ||
: JSON.stringify | ||
|
||
const localStorageValue = localStorage.getItem(key) | ||
if (localStorageValue !== null) { | ||
return deserializer(localStorageValue) | ||
} else { | ||
if (initialValue) { | ||
localStorage.setItem(key, serializer(initialValue)) | ||
} | ||
return initialValue | ||
} | ||
} catch { | ||
// If user is in private mode or has storage restriction | ||
// localStorage can throw. JSON.parse and JSON.stringify | ||
// can throw, too. | ||
return initialValue | ||
} | ||
}) | ||
|
||
// eslint-disable-next-line react-hooks/rules-of-hooks | ||
const [state, setState] = useState<T | undefined>(() => | ||
initializer.current(key) | ||
) | ||
|
||
// eslint-disable-next-line react-hooks/rules-of-hooks | ||
useLayoutEffect(() => setState(initializer.current(key)), [key]) | ||
|
||
// eslint-disable-next-line react-hooks/rules-of-hooks | ||
const set: Dispatch<SetStateAction<T | undefined>> = useCallback( | ||
(valOrFunc) => { | ||
try { | ||
const newState = | ||
typeof valOrFunc === 'function' | ||
? (valOrFunc as any)(state) | ||
: valOrFunc | ||
if (newState === undefined) return | ||
let value: string | ||
|
||
if (options) | ||
if (options.raw) | ||
if (typeof newState === 'string') value = newState | ||
else value = JSON.stringify(newState) | ||
else if (options.serializer) value = options.serializer(newState) | ||
else value = JSON.stringify(newState) | ||
else value = JSON.stringify(newState) | ||
|
||
localStorage.setItem(key, value) | ||
setState(deserializer(value)) | ||
} catch { | ||
// If user is in private mode or has storage restriction | ||
// localStorage can throw. Also JSON.stringify can throw. | ||
} | ||
}, | ||
// eslint-disable-next-line react-hooks/exhaustive-deps | ||
[key, setState] | ||
) | ||
|
||
// eslint-disable-next-line react-hooks/rules-of-hooks | ||
const remove = useCallback(() => { | ||
try { | ||
localStorage.removeItem(key) | ||
setState(undefined) | ||
} catch { | ||
// If user is in private mode or has storage restriction | ||
// localStorage can throw. | ||
} | ||
}, [key, setState]) | ||
|
||
return [state, set, remove] | ||
} |
Oops, something went wrong.