refactor(problem-editor): replace Zustand store with useRef store

This commit is contained in:
cfngc4594 2025-03-21 23:59:07 +08:00
parent 14bee1ade8
commit 8904e13a68

View File

@ -1,110 +1,66 @@
"use client"; "use client";
import type { import {
EditorLanguage, EditorLanguage,
EditorLanguageConfig, type EditorLanguageConfig,
LanguageServerConfig, type LanguageServerConfig,
Template, type Template,
} from "@prisma/client"; } from "@prisma/client";
import type { editor } from "monaco-editor"; import { useStore } from "zustand";
import { createStore, StoreApi, useStore } from "zustand"; import { type ReactNode, createContext, useRef, useContext } from "react";
import { persist, createJSONStorage } from "zustand/middleware"; import { type ProblemStore, createProblemStore } from "@/stores/problem-store";
import type { MonacoLanguageClient } from "monaco-languageclient";
import { DEFAULT_EDITOR_LANGUAGE } from "@/config/editor-language";
import { createContext, PropsWithChildren, useContext, useState } from "react";
type ProblemEditorState = { export type ProblemStoreApi = ReturnType<typeof createProblemStore>;
hydrated: boolean;
editor: editor.IStandaloneCodeEditor | null; export const ProblemStoreContext = createContext<ProblemStoreApi | undefined>(
monacoLanguageClient: MonacoLanguageClient | null; undefined
globalLang: EditorLanguage; );
currentLang: EditorLanguage;
currentValue: string; export interface ProblemStoreProviderProps {
children: ReactNode;
problemId: string; problemId: string;
templates: Template[]; templates: Template[];
editorLanguageConfigs: EditorLanguageConfig[]; editorLanguageConfigs: EditorLanguageConfig[];
languageServerConfigs: LanguageServerConfig[]; languageServerConfigs: LanguageServerConfig[];
}; }
type ProblemEditorActions = { export const ProblemStoreProvider = ({
setHydrated: (value: boolean) => void;
setEditor: (editor: editor.IStandaloneCodeEditor) => void;
setMonacoLanguageClient: (client: MonacoLanguageClient | null) => void;
setGlobalLang: (lang: EditorLanguage) => void;
setCurrentLang: (lang: EditorLanguage) => void;
setCurrentValue: (value: string) => void;
};
type ProblemEditorStore = ProblemEditorState & ProblemEditorActions;
const ProblemEditorContext = createContext<StoreApi<ProblemEditorStore> | undefined>(undefined);
type ProblemEditorProviderProps = PropsWithChildren & {
problemId: string;
templates: Template[];
editorLanguageConfigs: EditorLanguageConfig[];
languageServerConfigs: LanguageServerConfig[];
};
export function ProblemEditorProvider({
children, children,
problemId, problemId,
templates, templates,
editorLanguageConfigs, editorLanguageConfigs,
languageServerConfigs, languageServerConfigs,
}: ProblemEditorProviderProps) { }: ProblemStoreProviderProps) => {
const [store] = useState(() => const storeRef = useRef<ProblemStoreApi | null>(null);
createStore<ProblemEditorStore>()(
persist( if (storeRef.current === null) {
(set) => ({ storeRef.current = createProblemStore({
hydrated: false, hydrated: false,
editor: null, editor: null,
monacoLanguageClient: null, monacoLanguageClient: null,
globalLang: DEFAULT_EDITOR_LANGUAGE, globalLang: EditorLanguage.c,
currentLang: DEFAULT_EDITOR_LANGUAGE, currentLang: EditorLanguage.c,
currentValue: "", currentValue: "",
problemId, problemId,
templates, templates,
editorLanguageConfigs, editorLanguageConfigs,
languageServerConfigs, languageServerConfigs,
setHydrated: (value) => set({ hydrated: value }), });
setEditor: (editor) => set({ editor }),
setMonacoLanguageClient: (client) => set({ monacoLanguageClient: client }),
setGlobalLang: (lang) => set({ globalLang: lang }),
setCurrentLang: (lang) => set({ currentLang: lang }),
setCurrentValue: (value) => set({ currentValue: value }),
}),
{
name: "problem-store",
storage: createJSONStorage(() => localStorage),
partialize: (state) => ({
globalLang: state.globalLang,
}),
onRehydrateStorage: () => {
return (state, error) => {
if (error) {
console.error("An error happened during hydration", error);
} else if (state) {
state.setHydrated(true);
} }
};
},
}
)
)
);
return ( return (
<ProblemEditorContext.Provider value={store}> <ProblemStoreContext.Provider value={storeRef.current}>
{children} {children}
</ProblemEditorContext.Provider> </ProblemStoreContext.Provider>
); );
};
export const useProblemStore = <T,>(selector: (store: ProblemStore) => T): T => {
const problemStoreContext = useContext(ProblemStoreContext);
if (!problemStoreContext) {
throw new Error("useProblemStore must be used within ProblemStoreProvider");
} }
export function useProblemEditorStore<T>(selector: (state: ProblemEditorStore) => T) { return useStore(problemStoreContext, selector);
const context = useContext(ProblemEditorContext); };
if (!context) {
throw new Error("ProblemEditorContext.Provider is missing.");
}
return useStore(context, selector);
}