66 lines
1.8 KiB
TypeScript
66 lines
1.8 KiB
TypeScript
"use client";
|
|
/**
|
|
* UserContext:基于 XState actor context 和 selector 的 React Provider。
|
|
*/
|
|
import type { Dispatch, ReactNode } from "react";
|
|
import { createActorContext, shallowEqual } from "@xstate/react";
|
|
import type { SnapshotFrom } from "xstate";
|
|
|
|
import { userMachine } from "./user-machine";
|
|
import type { UserState as MachineContext, UserEvent } from "./user-machine";
|
|
|
|
/**
|
|
* 对外暴露的 State 形状(保持与原 UserState 兼容)
|
|
*/
|
|
interface UserState {
|
|
currentUser: MachineContext["currentUser"];
|
|
isLoading: boolean;
|
|
avatarUrl: string | null;
|
|
isVip: boolean;
|
|
creditBalance: number;
|
|
entitlements: MachineContext["entitlements"];
|
|
}
|
|
|
|
type UserSnapshot = SnapshotFrom<typeof userMachine>;
|
|
type UserSelector<T> = (snapshot: UserSnapshot) => T;
|
|
|
|
const UserActorContext = createActorContext(userMachine);
|
|
|
|
export interface UserProviderProps {
|
|
children: ReactNode;
|
|
}
|
|
|
|
export function UserProvider({ children }: UserProviderProps) {
|
|
return <UserActorContext.Provider>{children}</UserActorContext.Provider>;
|
|
}
|
|
|
|
export function useUserState(): UserState {
|
|
return useUserSelector(selectUserState, shallowEqual);
|
|
}
|
|
|
|
export function useUserDispatch(): Dispatch<UserEvent> {
|
|
return UserActorContext.useActorRef().send;
|
|
}
|
|
|
|
export function useUserSelector<T>(
|
|
selector: UserSelector<T>,
|
|
compare?: (previous: T, next: T) => boolean,
|
|
): T {
|
|
return UserActorContext.useSelector(selector, compare);
|
|
}
|
|
|
|
function selectUserState(state: UserSnapshot): UserState {
|
|
return {
|
|
currentUser: state.context.currentUser,
|
|
isLoading:
|
|
state.matches("initializing") ||
|
|
state.matches("fetching") ||
|
|
state.matches("loggingOut") ||
|
|
state.context.isLoading,
|
|
avatarUrl: state.context.avatarUrl,
|
|
isVip: state.context.isVip,
|
|
creditBalance: state.context.creditBalance,
|
|
entitlements: state.context.entitlements,
|
|
};
|
|
}
|