Skip to content

Commit

Permalink
feat: add internal useTrackedRefState & useVersionedAction hooks
Browse files Browse the repository at this point in the history
  • Loading branch information
vikiboss committed Aug 15, 2024
1 parent 840f5d7 commit 783fa7c
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ export * from './use-timeout-fn'
export * from './use-timeout'
export * from './use-title'
export * from './use-toggle'
export * from './use-tracked-ref-state'
export * from './use-tracked-effect'
export * from './use-unmount'
export * from './use-unmounted-ref'
Expand All @@ -139,6 +140,7 @@ export * from './use-update-layout-effect'
export * from './use-url-search-params'
export * from './use-user-idle'
export * from './use-user-media'
export * from './use-versioned-action'
export * from './use-web-observer'
export * from './use-window-focus'
export * from './use-window-scroll'
Expand Down
63 changes: 63 additions & 0 deletions src/use-tracked-ref-state/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { useRef } from 'react'
import { useCreation } from '../use-creation'
import { useRender } from '../use-render'
import { useStableFn } from '../use-stable-fn'
import { shallowEqual } from '../utils/equal'

export type UseTrackedRefStateRefState<S> = { [K in keyof S]: { used: boolean; value: S[K] } }

export function useTrackedRefState<S extends object, Keys extends keyof S = keyof S>(refState: S) {
const render = useRender()

const stateRef = useRef(
useCreation(() => {
const result = {} as UseTrackedRefStateRefState<S>
for (const key in refState) {
result[key] = { used: false, value: refState[key] }
}
return result
}),
)

const updateRefState = useStableFn(
<K extends keyof S>(
key: K,
newValue: S[K],
compare: (prevData: S[K], nextData: S[K]) => boolean = shallowEqual,
) => {
const refItem = stateRef.current[key]
if (shallowEqual(refItem.value, newValue)) return

const isValueChanged = !compare(refItem.value, newValue)

if (isValueChanged) {
refItem.value = newValue
refItem.used && render()
}
},
)

const markKeyAsUsed = useStableFn(<K extends Keys>(key: K) => {
stateRef.current[key].used = true
})

const actions = useCreation(() => ({
updateRefState,
markKeyAsUsed,
}))

const getters = useCreation(() => {
const result = {} as S
for (const key in stateRef.current) {
Object.defineProperty(result, key, {
get() {
markKeyAsUsed(key as unknown as Keys)
return stateRef.current[key].value
},
})
}
return result
})

return [getters, actions, stateRef.current] as const
}
17 changes: 17 additions & 0 deletions src/use-versioned-action/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { useRef } from 'react'
import { useStableFn } from '../use-stable-fn'

import type { AnyFunc } from '../utils/basic'

export function useVersionedAction() {
const versionRef = useRef(0)

const incVersion = useStableFn(() => ++versionRef.current)

const runVersionedAction = useStableFn((version: number, handler: AnyFunc) => {
if (version !== versionRef.current) return
return handler()
})

return [incVersion, runVersionedAction] as const
}

0 comments on commit 783fa7c

Please sign in to comment.