Skip to content

Commit

Permalink
fix: import react-use hooks and remove it as a dep
Browse files Browse the repository at this point in the history
  • Loading branch information
transitive-bullshit committed Nov 1, 2024
1 parent d721cc5 commit a43a40b
Show file tree
Hide file tree
Showing 4 changed files with 226 additions and 198 deletions.
3 changes: 1 addition & 2 deletions packages/react-notion-x/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,7 @@
"react-image": "^4.0.3",
"react-lazy-images": "^1.1.0",
"react-modal": "^3.14.3",
"react-pdf": "^9.1.1",
"react-use": "^17.5.1"
"react-pdf": "^9.1.1"
},
"devDependencies": {
"@types/lodash.throttle": "^4.1.6",
Expand Down
2 changes: 1 addition & 1 deletion packages/react-notion-x/src/third-party/collection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
getTextContent
} from 'notion-utils'
import * as React from 'react'
import { useLocalStorage, useWindowSize } from 'react-use'

import { PageIcon } from '../components/page-icon'
import {
Expand All @@ -17,6 +16,7 @@ import { CollectionViewIcon } from '../icons/collection-view-icon'
import { cs } from '../utils'
import { CollectionRow } from './collection-row'
import { CollectionView } from './collection-view'
import { useLocalStorage, useWindowSize } from './react-use'

const isServer = typeof window === 'undefined'

Expand Down
213 changes: 213 additions & 0 deletions packages/react-notion-x/src/third-party/react-use.ts
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]
}
Loading

0 comments on commit a43a40b

Please sign in to comment.