refactor(ProcessPostUseCase, useCursorPagination, useDifferentialPosts): enhance post merging and loading states
- Introduced new methods for merging posts and comments efficiently, improving performance during data updates. - Updated loading state management in hooks to include initial loading and loading more states for better user feedback. - Refactored HomeScreen and PostDetailScreen to utilize the new loading states and merging logic, enhancing user experience during data fetching. - Improved pagination handling in useCursorPagination and useDifferentialPosts to ensure consistent data management across components.
This commit is contained in:
@@ -109,6 +109,8 @@ class ProcessPostUseCase {
|
||||
// 当前用户ID
|
||||
private currentUserId: string | null = null;
|
||||
|
||||
private readonly mergePerfWarnThresholdMs = 12;
|
||||
|
||||
// 私有构造函数
|
||||
private constructor() {
|
||||
this.paginationManager = new PaginationStateManager();
|
||||
@@ -241,6 +243,83 @@ class ProcessPostUseCase {
|
||||
this.notifyStateChanged(key);
|
||||
}
|
||||
|
||||
private getEntityId(item: unknown): string | null {
|
||||
if (!item || typeof item !== 'object') {
|
||||
return null;
|
||||
}
|
||||
const maybeId = (item as { id?: unknown }).id;
|
||||
if (typeof maybeId === 'string' || typeof maybeId === 'number') {
|
||||
return String(maybeId);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private mergePostListWithFastPath(previousInput: Post[] | null | undefined, incomingInput: Post[] | null | undefined): Post[] {
|
||||
const previous = Array.isArray(previousInput) ? previousInput : [];
|
||||
const incoming = Array.isArray(incomingInput) ? incomingInput : [];
|
||||
if (incoming.length === 0) {
|
||||
return previous;
|
||||
}
|
||||
if (previous.length === 0) {
|
||||
return incoming;
|
||||
}
|
||||
|
||||
const lastPrevId = this.getEntityId(previous[previous.length - 1]);
|
||||
const firstIncomingId = this.getEntityId(incoming[0]);
|
||||
if (lastPrevId && firstIncomingId && lastPrevId !== firstIncomingId) {
|
||||
const prevIdSet = new Set(previous.map((item) => this.getEntityId(item)).filter(Boolean) as string[]);
|
||||
if (!prevIdSet.has(firstIncomingId)) {
|
||||
return [...previous, ...incoming];
|
||||
}
|
||||
}
|
||||
|
||||
const merged = [...previous];
|
||||
const indexById = new Map<string, number>();
|
||||
for (let i = 0; i < merged.length; i += 1) {
|
||||
const id = this.getEntityId(merged[i]);
|
||||
if (id) {
|
||||
indexById.set(id, i);
|
||||
}
|
||||
}
|
||||
|
||||
for (const post of incoming) {
|
||||
const id = this.getEntityId(post);
|
||||
if (!id) {
|
||||
merged.push(post);
|
||||
continue;
|
||||
}
|
||||
const existingIndex = indexById.get(id);
|
||||
if (existingIndex === undefined) {
|
||||
indexById.set(id, merged.length);
|
||||
merged.push(post);
|
||||
} else {
|
||||
merged[existingIndex] = post;
|
||||
}
|
||||
}
|
||||
|
||||
return merged;
|
||||
}
|
||||
|
||||
private mergeRefreshWindow(previousInput: Post[] | null | undefined, latestInput: Post[] | null | undefined): Post[] {
|
||||
const previous = Array.isArray(previousInput) ? previousInput : [];
|
||||
const latest = Array.isArray(latestInput) ? latestInput : [];
|
||||
if (latest.length === 0) {
|
||||
return previous;
|
||||
}
|
||||
if (previous.length === 0) {
|
||||
return latest;
|
||||
}
|
||||
|
||||
const latestIdSet = new Set(
|
||||
latest.map((item) => this.getEntityId(item)).filter(Boolean) as string[]
|
||||
);
|
||||
const remaining = previous.filter((item) => {
|
||||
const id = this.getEntityId(item);
|
||||
return !id || !latestIdSet.has(id);
|
||||
});
|
||||
return [...latest, ...remaining];
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新帖子详情状态
|
||||
* @param postId 帖子ID
|
||||
@@ -280,8 +359,17 @@ class ProcessPostUseCase {
|
||||
const isCursorMode = result.nextCursor !== undefined && result.nextCursor !== null;
|
||||
|
||||
// 更新状态(保存请求参数以便加载更多时使用)
|
||||
const mergeStart = Date.now();
|
||||
const currentState = this.getPostsState(key);
|
||||
const incomingPosts = Array.isArray(result.posts) ? result.posts : [];
|
||||
const mergedPosts = this.mergeRefreshWindow(currentState.posts, incomingPosts);
|
||||
const mergeCost = Date.now() - mergeStart;
|
||||
if (mergeCost >= this.mergePerfWarnThresholdMs) {
|
||||
console.log('[PostPerf] fetchPosts merge cost', { key, costMs: mergeCost, prev: currentState.posts.length, next: incomingPosts.length });
|
||||
}
|
||||
|
||||
this.updatePostsState(key, {
|
||||
posts: result.posts,
|
||||
posts: mergedPosts,
|
||||
hasMore: result.hasMore,
|
||||
cursor: isCursorMode ? result.nextCursor : null, // cursor 模式保存 cursor,否则设为 null 表示 page 模式
|
||||
currentPage: isCursorMode ? undefined : 1, // page 模式从第 1 页开始
|
||||
@@ -293,7 +381,7 @@ class ProcessPostUseCase {
|
||||
// 通知订阅者
|
||||
this.notifySubscribers({
|
||||
type: 'posts_loaded',
|
||||
payload: { key, posts: result.posts, hasMore: result.hasMore },
|
||||
payload: { key, posts: incomingPosts, hasMore: result.hasMore },
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
|
||||
@@ -338,8 +426,16 @@ class ProcessPostUseCase {
|
||||
const isCursorMode = result.nextCursor !== undefined && result.nextCursor !== null;
|
||||
|
||||
// 更新状态
|
||||
const mergeStart = Date.now();
|
||||
const incomingPosts = Array.isArray(result.posts) ? result.posts : [];
|
||||
const mergedPosts = this.mergeRefreshWindow(state.posts, incomingPosts);
|
||||
const mergeCost = Date.now() - mergeStart;
|
||||
if (mergeCost >= this.mergePerfWarnThresholdMs) {
|
||||
console.log('[PostPerf] refreshPosts merge cost', { key, costMs: mergeCost, prev: state.posts.length, next: incomingPosts.length });
|
||||
}
|
||||
|
||||
this.updatePostsState(key, {
|
||||
posts: result.posts,
|
||||
posts: mergedPosts,
|
||||
hasMore: result.hasMore,
|
||||
cursor: isCursorMode ? result.nextCursor : null,
|
||||
currentPage: isCursorMode ? undefined : 1,
|
||||
@@ -417,7 +513,13 @@ class ProcessPostUseCase {
|
||||
const result = await postRepository.getPosts(queryParams);
|
||||
|
||||
// 合并新数据
|
||||
const newPosts = [...state.posts, ...result.posts];
|
||||
const mergeStart = Date.now();
|
||||
const incomingPosts = Array.isArray(result.posts) ? result.posts : [];
|
||||
const newPosts = this.mergePostListWithFastPath(state.posts, incomingPosts);
|
||||
const mergeCost = Date.now() - mergeStart;
|
||||
if (mergeCost >= this.mergePerfWarnThresholdMs) {
|
||||
console.log('[PostPerf] loadMore merge cost', { key, costMs: mergeCost, prev: state.posts.length, incoming: incomingPosts.length, merged: newPosts.length });
|
||||
}
|
||||
|
||||
// 判断响应是否为 cursor 模式(有 next_cursor 返回)
|
||||
const responseIsCursorMode = result.nextCursor !== undefined && result.nextCursor !== null;
|
||||
@@ -433,7 +535,7 @@ class ProcessPostUseCase {
|
||||
});
|
||||
|
||||
return {
|
||||
posts: result.posts,
|
||||
posts: incomingPosts,
|
||||
hasMore: result.hasMore,
|
||||
nextCursor: result.nextCursor,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user