one
Version:
One is a new React Framework that makes Vite serve both native and web.
76 lines (65 loc) • 2.1 kB
text/typescript
'use client'
import type { ParamListBase } from '@react-navigation/core'
import type { StackNavigationState } from '@react-navigation/routers'
import {
createContext,
createElement,
useContext,
useMemo,
type PropsWithChildren,
type ReactElement,
} from 'react'
import type { UseStackResult } from './types'
import { getPathFromState } from '../fork/getPathFromState'
import { exposeStackToolbarConfig } from '../layouts/stack-utils/StackToolbar.shared'
import { getResolvedLinking } from '../router/linkingConfig'
export type HeadlessStackDescriptors = Record<
string,
{
options: Record<string, any>
render: () => ReactElement
}
>
type StackStateProviderProps = PropsWithChildren<{
state: StackNavigationState<ParamListBase>
descriptors: HeadlessStackDescriptors
}>
const StackContext = createContext<UseStackResult | null>(null)
export function StackStateProvider({
state,
descriptors,
children,
}: StackStateProviderProps) {
const linking = getResolvedLinking()
const value = useMemo<UseStackResult>(() => {
const screens = state.routes.map((route, index) => {
const descriptor = descriptors[route.key]!
const options = exposeStackToolbarConfig(descriptor.options)
const href = (linking?.getPathFromState ?? getPathFromState)(
{ ...state, index },
linking?.config
)
return {
key: route.key,
name: route.name,
params: (route.params ?? {}) as Record<string, any>,
href,
isFocused: index === state.index,
keepMounted: options.keepMounted === true,
options,
element: descriptor.render(),
}
})
return { screens, focused: screens[state.index]! }
}, [descriptors, linking, state])
return createElement(StackContext.Provider, { value }, children)
}
export function useStack(): UseStackResult {
const value = useContext(StackContext)
if (!value) {
throw new Error(
'useStack() must be called inside a <Stack>. it is web-only: on native the stack is rendered by react-navigation.'
)
}
return value
}