diff --git a/packages/src/context/context.ts b/packages/src/context/context.ts index ce99b3c..068ee4c 100644 --- a/packages/src/context/context.ts +++ b/packages/src/context/context.ts @@ -3,10 +3,20 @@ import { createSafeContext } from '../utils/create-safe-context'; export const [OverlayContextProvider, useOverlayContext] = createSafeContext('overlay-kit/OverlayContext'); +function useSafeOverlayContext() { + const overlayContext = useOverlayContext(); + + if (overlayContext == null) { + throw new Error('This component must be used inside a component.'); + } + + return overlayContext; +} + export function useCurrentOverlay() { - return useOverlayContext().current; + return useSafeOverlayContext().current; } export function useOverlayData() { - return useOverlayContext().overlayData; + return useSafeOverlayContext().overlayData; } diff --git a/packages/src/context/provider.tsx b/packages/src/context/provider.tsx index 7847b32..505b012 100644 --- a/packages/src/context/provider.tsx +++ b/packages/src/context/provider.tsx @@ -7,6 +7,10 @@ import { overlay } from '../event'; export function OverlayProvider({ children }: PropsWithChildren) { const overlayState = useSyncOverlayStore(); + if (overlayState == null) { + dispatchOverlay({ type: 'INIT' }); + } + useEffect(() => { return () => { dispatchOverlay({ type: 'REMOVE_ALL' }); @@ -16,26 +20,27 @@ export function OverlayProvider({ children }: PropsWithChildren) { return ( {children} - {overlayState.overlayOrderList.map((item) => { - const { id: currentOverlayId, isOpen, controller: currentController } = overlayState.overlayData[item]; + {overlayState != null && + overlayState.overlayOrderList.map((item) => { + const { id: currentOverlayId, isOpen, controller: currentController } = overlayState.overlayData[item]; - return ( - { - requestAnimationFrame(() => { - dispatchOverlay({ type: 'OPEN', overlayId: currentOverlayId }); - }); - }} - onCloseModal={() => overlay.close(currentOverlayId)} - onExitModal={() => overlay.unmount(currentOverlayId)} - controller={currentController} - /> - ); - })} + return ( + { + requestAnimationFrame(() => { + dispatchOverlay({ type: 'OPEN', overlayId: currentOverlayId }); + }); + }} + onCloseModal={() => overlay.close(currentOverlayId)} + onExitModal={() => overlay.unmount(currentOverlayId)} + controller={currentController} + /> + ); + })} ); } diff --git a/packages/src/context/reducer.ts b/packages/src/context/reducer.ts index 53b462e..aa41c1c 100644 --- a/packages/src/context/reducer.ts +++ b/packages/src/context/reducer.ts @@ -1,6 +1,7 @@ import { type OverlayData, type OverlayItem } from './store'; export type OverlayReducerAction = + | { type: 'INIT' } | { type: 'ADD'; overlay: OverlayItem } | { type: 'OPEN'; overlayId: string } | { type: 'CLOSE'; overlayId: string } @@ -9,6 +10,18 @@ export type OverlayReducerAction = | { type: 'REMOVE_ALL' }; export function overlayReducer(state: OverlayData, action: OverlayReducerAction): OverlayData { + if (state == null) { + if (action.type !== 'INIT') { + throw new Error('This component must be used inside a component.'); + } + + return { + current: null, + overlayOrderList: [], + overlayData: {}, + }; + } + switch (action.type) { case 'ADD': { const isExisted = state.overlayOrderList.includes(action.overlay.id); diff --git a/packages/src/context/store.ts b/packages/src/context/store.ts index 1991322..c0228c4 100644 --- a/packages/src/context/store.ts +++ b/packages/src/context/store.ts @@ -7,17 +7,18 @@ export type OverlayItem = { isOpen: boolean; controller: OverlayControllerComponent; }; -export type OverlayData = { - current: OverlayId | null; - overlayOrderList: OverlayId[]; - overlayData: Record; -}; +export type OverlayData = + | { + current: OverlayId | null; + overlayOrderList: OverlayId[]; + overlayData: Record; + } + | undefined; -let overlays: OverlayData = { - current: null, - overlayOrderList: [], - overlayData: {}, -}; +/** + * @description Using `OverlayProvider` will initialize the variable + */ +let overlays: OverlayData = undefined; let listeners: Array<() => void> = []; function emitChangeListener() {