refactor: scope providers by route and use XState selectors
This commit is contained in:
@@ -1,13 +1,8 @@
|
||||
"use client";
|
||||
|
||||
import {
|
||||
type Dispatch,
|
||||
type ReactNode,
|
||||
createContext,
|
||||
useContext,
|
||||
useMemo,
|
||||
} from "react";
|
||||
import { useMachine } from "@xstate/react";
|
||||
import type { Dispatch, ReactNode } from "react";
|
||||
import { createActorContext, shallowEqual } from "@xstate/react";
|
||||
import type { SnapshotFrom } from "xstate";
|
||||
|
||||
import { privateRoomMachine } from "./private-room-machine";
|
||||
import type {
|
||||
@@ -33,64 +28,54 @@ export interface PrivateRoomContextState {
|
||||
isUnlocking: boolean;
|
||||
}
|
||||
|
||||
const PrivateRoomStateCtx = createContext<PrivateRoomContextState | null>(null);
|
||||
const PrivateRoomDispatchCtx = createContext<Dispatch<PrivateRoomEvent> | null>(
|
||||
null,
|
||||
);
|
||||
type PrivateRoomSnapshot = SnapshotFrom<typeof privateRoomMachine>;
|
||||
type PrivateRoomSelector<T> = (snapshot: PrivateRoomSnapshot) => T;
|
||||
|
||||
const PrivateRoomActorContext = createActorContext(privateRoomMachine);
|
||||
|
||||
export interface PrivateRoomProviderProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export function PrivateRoomProvider({ children }: PrivateRoomProviderProps) {
|
||||
const [state, send] = useMachine(privateRoomMachine);
|
||||
|
||||
const privateRoomState = useMemo<PrivateRoomContextState>(
|
||||
() => ({
|
||||
status: String(state.value),
|
||||
profile: state.context.profile,
|
||||
items: state.context.items,
|
||||
nextCursor: state.context.nextCursor,
|
||||
hasMore: state.context.hasMore,
|
||||
creditBalance: state.context.creditBalance,
|
||||
errorMessage: state.context.errorMessage,
|
||||
unlockingMomentId: state.context.unlockingMomentId,
|
||||
unlockErrorMessage: state.context.unlockErrorMessage,
|
||||
pendingConfirmMomentId: state.context.pendingConfirmMomentId,
|
||||
unlockPaywallRequest: state.context.unlockPaywallRequest,
|
||||
unlockSuccessNonce: state.context.unlockSuccessNonce,
|
||||
isLoading: state.matches("loading"),
|
||||
isLoadingMore: state.matches("loadingMore"),
|
||||
isUnlocking: state.matches("unlocking"),
|
||||
}),
|
||||
[state],
|
||||
);
|
||||
|
||||
return (
|
||||
<PrivateRoomStateCtx.Provider value={privateRoomState}>
|
||||
<PrivateRoomDispatchCtx.Provider value={send}>
|
||||
{children}
|
||||
</PrivateRoomDispatchCtx.Provider>
|
||||
</PrivateRoomStateCtx.Provider>
|
||||
<PrivateRoomActorContext.Provider>{children}</PrivateRoomActorContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function usePrivateRoomState(): PrivateRoomContextState {
|
||||
const ctx = useContext(PrivateRoomStateCtx);
|
||||
if (!ctx) {
|
||||
throw new Error(
|
||||
"usePrivateRoomState must be used inside <PrivateRoomProvider>",
|
||||
);
|
||||
}
|
||||
return ctx;
|
||||
return usePrivateRoomSelector(selectPrivateRoomState, shallowEqual);
|
||||
}
|
||||
|
||||
export function usePrivateRoomDispatch(): Dispatch<PrivateRoomEvent> {
|
||||
const ctx = useContext(PrivateRoomDispatchCtx);
|
||||
if (!ctx) {
|
||||
throw new Error(
|
||||
"usePrivateRoomDispatch must be used inside <PrivateRoomProvider>",
|
||||
);
|
||||
}
|
||||
return ctx;
|
||||
return PrivateRoomActorContext.useActorRef().send;
|
||||
}
|
||||
|
||||
export function usePrivateRoomSelector<T>(
|
||||
selector: PrivateRoomSelector<T>,
|
||||
compare?: (previous: T, next: T) => boolean,
|
||||
): T {
|
||||
return PrivateRoomActorContext.useSelector(selector, compare);
|
||||
}
|
||||
|
||||
function selectPrivateRoomState(
|
||||
state: PrivateRoomSnapshot,
|
||||
): PrivateRoomContextState {
|
||||
return {
|
||||
status: String(state.value),
|
||||
profile: state.context.profile,
|
||||
items: state.context.items,
|
||||
nextCursor: state.context.nextCursor,
|
||||
hasMore: state.context.hasMore,
|
||||
creditBalance: state.context.creditBalance,
|
||||
errorMessage: state.context.errorMessage,
|
||||
unlockingMomentId: state.context.unlockingMomentId,
|
||||
unlockErrorMessage: state.context.unlockErrorMessage,
|
||||
pendingConfirmMomentId: state.context.pendingConfirmMomentId,
|
||||
unlockPaywallRequest: state.context.unlockPaywallRequest,
|
||||
unlockSuccessNonce: state.context.unlockSuccessNonce,
|
||||
isLoading: state.matches("loading"),
|
||||
isLoadingMore: state.matches("loadingMore"),
|
||||
isUnlocking: state.matches("unlocking"),
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user