commandsHooks.ts69 lines · main
1'use client'
2
3import { isEqual } from 'lodash'
4import { useEffect, useMemo, useRef } from 'react'
5import { useSnapshot } from 'valtio'
6
7import { useCommandContext } from '../../internal/Context'
8import { isCommandsPage, PageDefinition } from '../../internal/state/pagesState'
9import type { CommandOptions, ICommand } from '../types'
10import { useCurrentPage } from './pagesHooks'
11
12const useCommands = () => {
13 const { commandsState } = useCommandContext()
14 const { commandSections } = useSnapshot(commandsState)
15
16 const _currPage = useCurrentPage()
17 const currPage = _currPage as PageDefinition
18 if (currPage && isCommandsPage(currPage)) return currPage.sections
19
20 return commandSections
21}
22
23const useRegisterCommands = (
24 sectionName: string,
25 commands: ICommand[],
26 options: CommandOptions = {}
27) => {
28 const { commandsState } = useCommandContext()
29 const { registerSection } = useSnapshot(commandsState)
30
31 const prevDeps = useRef(options?.deps)
32 options.enabled ??= true
33 const prevEnabled = useRef<boolean | undefined>(options.enabled)
34
35 const unsubscribe = useRef<(() => void) | undefined>(undefined)
36
37 /**
38 * useEffect handles the registration on first render, since React runs the
39 * first render twice in development. (Otherwise the first render would leave
40 * a dangling subscription.)
41 *
42 * It also handles final cleanup, since useMemo can't do this.
43 *
44 * useMemo handles the registration on subsequent renders, to ensure it
45 * happens synchronously.
46 */
47 useMemo(() => {
48 if (!isEqual(prevDeps.current, options.deps) || prevEnabled.current !== options.enabled) {
49 unsubscribe.current?.()
50
51 unsubscribe.current = options.enabled
52 ? registerSection(sectionName, commands, options)
53 : undefined
54
55 prevDeps.current = options.deps
56 prevEnabled.current = options.enabled
57 }
58 }, [registerSection, sectionName, commands, options])
59
60 useEffect(() => {
61 unsubscribe.current = options.enabled
62 ? registerSection(sectionName, commands, options)
63 : undefined
64
65 return () => unsubscribe.current?.()
66 }, [])
67}
68
69export { useCommands, useRegisterCommands }