memos/web/src/hooks/useMemoSorting.ts
Steven fae5eac31b fix(web): fix infinite loop in MemoEditor and improve React/MobX integration
- Wrap all setter functions in useMemoEditorState with useCallback to ensure stable references
  This prevents infinite loops when setters are used in useEffect dependencies (fixes "Maximum update depth exceeded" error)
- Extract MobX observable values in useMemoFilters and useMemoSorting before using them in useMemo dependencies
  This prevents React from tracking MobX observables directly, improving reliability
- Add comprehensive documentation explaining the design decisions for future maintainability

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-01 08:54:40 +08:00

49 lines
1.6 KiB
TypeScript

import dayjs from "dayjs";
import { useMemo } from "react";
import { viewStore } from "@/store";
import { State } from "@/types/proto/api/v1/common";
import { Memo } from "@/types/proto/api/v1/memo_service";
export interface UseMemoSortingOptions {
pinnedFirst?: boolean;
state?: State;
}
export interface UseMemoSortingResult {
listSort: (memos: Memo[]) => Memo[];
orderBy: string;
}
export const useMemoSorting = (options: UseMemoSortingOptions = {}): UseMemoSortingResult => {
const { pinnedFirst = false, state = State.NORMAL } = options;
// Extract MobX observable values to avoid issues with React dependency tracking
const orderByTimeAsc = viewStore.state.orderByTimeAsc;
// Generate orderBy string for API
const orderBy = useMemo(() => {
const timeOrder = orderByTimeAsc ? "display_time asc" : "display_time desc";
return pinnedFirst ? `pinned desc, ${timeOrder}` : timeOrder;
}, [pinnedFirst, orderByTimeAsc]);
// Generate listSort function for client-side sorting
const listSort = useMemo(() => {
return (memos: Memo[]): Memo[] => {
return memos
.filter((memo) => memo.state === state)
.sort((a, b) => {
// First, sort by pinned status if enabled
if (pinnedFirst && a.pinned !== b.pinned) {
return b.pinned ? 1 : -1;
}
// Then sort by display time
return orderByTimeAsc
? dayjs(a.displayTime).unix() - dayjs(b.displayTime).unix()
: dayjs(b.displayTime).unix() - dayjs(a.displayTime).unix();
});
};
}, [pinnedFirst, state, orderByTimeAsc]);
return { listSort, orderBy };
};