perf(reader): split progress into its own store to cut React commit storm (#4557)
setProgress was called multiple times per swipe burst, each call writing into readerStore.viewStates[key].progress. ~65 places in the reader subtree subscribed to useReaderStore() without a selector, so every setProgress fan-out re-rendered all of them -- even the 51 that didn't care about progress. On Android release builds this showed up as Layout = 9.8% and Function Call = 9.6% of main-thread self time in Chrome DevTools' Bottom-Up profile during a reading session. Fix: - New tiny store store/readerProgressStore.ts holds the per-book BookProgress map. setBookProgress only fires its own subscribers. - readerStore.setProgress now writes progress to the new store and only touches bookDataStore for the primary view (secondary parallel views shouldn't overwrite the shared config). - readerStore.getProgress is kept as a delegating facade so existing imperative call sites don't break. - Components / hooks that genuinely need to react to progress changes subscribe via the new useBookProgress(bookKey) hook. The handful of call sites that just want a one-shot read use getBookProgress(key) so they don't subscribe at all. - readerStore.clearViewState calls clearBookProgress so the map doesn't grow unbounded across book opens/closes. See store/readerProgressStore.ts header for the full rationale.
This commit is contained in:
@@ -4,6 +4,7 @@ import { useAuth } from '@/context/AuthContext';
|
||||
import { useThemeStore } from '@/store/themeStore';
|
||||
import { useBookDataStore } from '@/store/bookDataStore';
|
||||
import { useReaderStore } from '@/store/readerStore';
|
||||
import { useBookProgress } from '@/store/readerProgressStore';
|
||||
import { useProofreadStore } from '@/store/proofreadStore';
|
||||
import { TransformContext } from '@/services/transformers/types';
|
||||
import { proofreadTransformer } from '@/services/transformers/proofread';
|
||||
@@ -30,9 +31,12 @@ export const useTTSControl = ({ bookKey, onRequestHidePanel }: UseTTSControlProp
|
||||
const { appService } = useEnv();
|
||||
const { user } = useAuth();
|
||||
const { isDarkMode } = useThemeStore();
|
||||
const { getBookData } = useBookDataStore();
|
||||
const { getView, getProgress, getViewSettings } = useReaderStore();
|
||||
const { setViewSettings, setTTSEnabled } = useReaderStore();
|
||||
const getBookData = useBookDataStore((s) => s.getBookData);
|
||||
const getView = useReaderStore((s) => s.getView);
|
||||
const getProgress = useReaderStore((s) => s.getProgress);
|
||||
const getViewSettings = useReaderStore((s) => s.getViewSettings);
|
||||
const setViewSettings = useReaderStore((s) => s.setViewSettings);
|
||||
const setTTSEnabled = useReaderStore((s) => s.setTTSEnabled);
|
||||
const { getMergedRules } = useProofreadStore();
|
||||
|
||||
const [ttsLang, setTtsLang] = useState<string>('en');
|
||||
@@ -265,8 +269,10 @@ export const useTTSControl = ({ bookKey, onRequestHidePanel }: UseTTSControlProp
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [ttsController, bookKey]);
|
||||
|
||||
// Location tracking — re-highlight when progress changes
|
||||
const progress = getProgress(bookKey);
|
||||
// Location tracking — re-highlight when progress changes.
|
||||
// Reactive subscription via readerProgressStore so the effect below
|
||||
// re-runs on page turns without dragging in the whole readerStore.
|
||||
const progress = useBookProgress(bookKey);
|
||||
useEffect(() => {
|
||||
const ttsController = ttsControllerRef.current;
|
||||
if (!ttsController) return;
|
||||
|
||||
Reference in New Issue
Block a user