refactor: migrate state imports from contexts to stores directory
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
"use client";
|
||||
/**
|
||||
* ChatContext:基于 XState v5 的 React Context Provider
|
||||
*
|
||||
* 业务事件通过 `useMachine(chatMachine).send()` 派发。
|
||||
* XState 自动处理 HTTP 副作用(invoke + fromPromise actors)。
|
||||
*
|
||||
* 兼容性:保持原 `useChatState()` / `useChatDispatch()` API。
|
||||
* 内部将 XState 状态机快照映射回原 `ChatState` 形状。
|
||||
*
|
||||
* 注:WebSocket 长生命周期 actor 暂未集成到状态机(保留在 chat-side-effects.ts
|
||||
* 由外部 chat-side-effects 模块管理),待下一轮迁移。
|
||||
*/
|
||||
import {
|
||||
type Dispatch,
|
||||
type ReactNode,
|
||||
createContext,
|
||||
useContext,
|
||||
useMemo,
|
||||
} from "react";
|
||||
import { useMachine } from "@xstate/react";
|
||||
|
||||
import { chatMachine } from "./chat-machine";
|
||||
import type { ChatEvent, ChatContext as MachineContext } from "./chat-machine";
|
||||
|
||||
/**
|
||||
* 对外暴露的 State 形状(保持与原 ChatState 兼容)
|
||||
*/
|
||||
export interface ChatState {
|
||||
messages: MachineContext["messages"];
|
||||
isReplyingAI: boolean;
|
||||
isGuest: boolean;
|
||||
guestRemainingQuota: number;
|
||||
guestTotalQuota: number;
|
||||
quotaExceededTrigger: number;
|
||||
isLoadingMore: boolean;
|
||||
hasMore: boolean;
|
||||
historyOffset: number;
|
||||
}
|
||||
|
||||
const ChatStateCtx = createContext<ChatState | null>(null);
|
||||
const ChatDispatchCtx = createContext<Dispatch<ChatEvent> | null>(null);
|
||||
|
||||
export interface ChatProviderProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export function ChatProvider({ children }: ChatProviderProps) {
|
||||
const [state, send] = useMachine(chatMachine);
|
||||
|
||||
// 映射 XState 状态机快照 → 原 ChatState 形状
|
||||
const chatState = useMemo<ChatState>(
|
||||
() => ({
|
||||
messages: state.context.messages,
|
||||
isReplyingAI: state.context.isReplyingAI,
|
||||
isGuest: state.context.isGuest,
|
||||
guestRemainingQuota: state.context.guestRemainingQuota,
|
||||
guestTotalQuota: state.context.guestTotalQuota,
|
||||
quotaExceededTrigger: state.context.quotaExceededTrigger,
|
||||
isLoadingMore: state.context.isLoadingMore,
|
||||
hasMore: state.context.hasMore,
|
||||
historyOffset: state.context.historyOffset,
|
||||
}),
|
||||
[state],
|
||||
);
|
||||
|
||||
return (
|
||||
<ChatStateCtx.Provider value={chatState}>
|
||||
<ChatDispatchCtx.Provider value={send}>{children}</ChatDispatchCtx.Provider>
|
||||
</ChatStateCtx.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function useChatState(): ChatState {
|
||||
const ctx = useContext(ChatStateCtx);
|
||||
if (!ctx) throw new Error("useChatState must be used inside <ChatProvider>");
|
||||
return ctx;
|
||||
}
|
||||
|
||||
export function useChatDispatch(): Dispatch<ChatEvent> {
|
||||
const ctx = useContext(ChatDispatchCtx);
|
||||
if (!ctx)
|
||||
throw new Error("useChatDispatch must be used inside <ChatProvider>");
|
||||
return ctx;
|
||||
}
|
||||
Reference in New Issue
Block a user