refactor(core): optimize state management and component rendering performance
Improve application stability and performance by optimizing Zustand store usage, implementing memoization patterns, and introducing persistent authentication. - **State Management**: - Refactor Zustand selectors to use `useShallow` and `getState()` to prevent unnecessary re-renders and infinite loops in hooks. - Implement `persist` middleware for `authStore` to maintain user sessions across restarts. - Introduce `buildStateCached` in `themeStore` to reduce redundant theme object computations. - **Performance & Rendering**: - Implement `useMemo` for stable object/array references in `ImageGallery` and list components to prevent expensive re-renders. - Replace inline arrow functions with `useCallback` in complex screens like `ChatScreen` and `MessageListScreen`. - Optimize `FlashList` usage by providing stable `key` and `extraData` props. - **Architecture**: - Decouple unread count fetching by introducing `useUnreadCountQuery` (React Query) for better caching and synchronization. - Add `useChannels` hook to centralize channel data fetching. - Refine `SessionGate` logic to allow immediate rendering of authenticated users while verifying in the background.
This commit is contained in:
@@ -111,6 +111,11 @@ export const ChatScreen: React.FC<ChatScreenProps> = (props) => {
|
||||
const [chatImages, setChatImages] = useState<ImageGridItem[]>([]);
|
||||
const [selectedImageIndex, setSelectedImageIndex] = useState(0);
|
||||
|
||||
const stableGalleryImages = useMemo(() => chatImages.map((img, i) => ({
|
||||
id: img.id || img.url || `img-${i}`,
|
||||
url: img.url || img.uri || ''
|
||||
})), [chatImages]);
|
||||
|
||||
// 图片点击处理函数
|
||||
const handleImagePress = (images: ImageGridItem[], index: number) => {
|
||||
setChatImages(images);
|
||||
@@ -212,6 +217,28 @@ export const ChatScreen: React.FC<ChatScreenProps> = (props) => {
|
||||
jumpToLatestMessages,
|
||||
setBrowsingHistory,
|
||||
} = useChatScreen(props);
|
||||
|
||||
const stableOnBack = useCallback(() => router.back(), [router]);
|
||||
const stableOnFocusInput = useCallback(() => {
|
||||
if (activePanel !== 'none' && activePanel !== 'mention') {
|
||||
closePanel();
|
||||
}
|
||||
}, [activePanel, closePanel]);
|
||||
const stableFocusTextInput = useCallback(() => textInputRef.current?.focus(), []);
|
||||
const stableOnLayout = useCallback((e: any) => {
|
||||
scrollPositionRef.current.viewportHeight = e.nativeEvent.layout.height;
|
||||
}, []);
|
||||
const stableOnScrollBeginDrag = useCallback(() => {
|
||||
isUserDraggingRef.current = true;
|
||||
handleDismiss();
|
||||
}, [handleDismiss]);
|
||||
const stableOnScrollEndDrag = useCallback(() => {
|
||||
isUserDraggingRef.current = false;
|
||||
}, []);
|
||||
const stableOnMomentumScrollEnd = useCallback(() => {
|
||||
isUserDraggingRef.current = false;
|
||||
}, []);
|
||||
|
||||
const displayMessages = useMemo(() => [...messages].reverse(), [messages]);
|
||||
|
||||
const longPressMenuMemberMap = useMemo(() => {
|
||||
@@ -460,7 +487,7 @@ export const ChatScreen: React.FC<ChatScreenProps> = (props) => {
|
||||
otherUser={otherUser}
|
||||
routeGroupName={effectiveGroupName ?? undefined}
|
||||
typingHint={typingHint}
|
||||
onBack={props.onEmbeddedBack ? props.onEmbeddedBack : () => router.back()}
|
||||
onBack={props.onEmbeddedBack || stableOnBack}
|
||||
onTitlePress={navigateToInfo}
|
||||
onMorePress={navigateToChatSettings}
|
||||
onGroupInfoPress={handleGroupInfoPress}
|
||||
@@ -470,9 +497,7 @@ export const ChatScreen: React.FC<ChatScreenProps> = (props) => {
|
||||
{/* 消息列表 */}
|
||||
<View
|
||||
style={styles.messageListContainer}
|
||||
onLayout={e => {
|
||||
scrollPositionRef.current.viewportHeight = e.nativeEvent.layout.height;
|
||||
}}
|
||||
onLayout={stableOnLayout}
|
||||
onTouchEnd={handleDismiss}
|
||||
>
|
||||
{loading ? (
|
||||
@@ -493,16 +518,9 @@ export const ChatScreen: React.FC<ChatScreenProps> = (props) => {
|
||||
scrollEnabled={true}
|
||||
drawDistance={250}
|
||||
onScroll={handleMessageListScroll}
|
||||
onScrollBeginDrag={() => {
|
||||
isUserDraggingRef.current = true;
|
||||
handleDismiss();
|
||||
}}
|
||||
onScrollEndDrag={() => {
|
||||
isUserDraggingRef.current = false;
|
||||
}}
|
||||
onMomentumScrollEnd={() => {
|
||||
isUserDraggingRef.current = false;
|
||||
}}
|
||||
onScrollBeginDrag={stableOnScrollBeginDrag}
|
||||
onScrollEndDrag={stableOnScrollEndDrag}
|
||||
onMomentumScrollEnd={stableOnMomentumScrollEnd}
|
||||
scrollEventThrottle={16}
|
||||
onContentSizeChange={handleContentSizeChange}
|
||||
/>
|
||||
@@ -568,12 +586,7 @@ export const ChatScreen: React.FC<ChatScreenProps> = (props) => {
|
||||
pendingAttachments={pendingAttachments}
|
||||
onRemovePendingAttachment={removePendingAttachment}
|
||||
onCancelReply={handleCancelReply}
|
||||
onFocus={() => {
|
||||
// 输入框获得焦点时,关闭其他面板(但不要关闭键盘)
|
||||
if (activePanel !== 'none' && activePanel !== 'mention') {
|
||||
closePanel();
|
||||
}
|
||||
}}
|
||||
onFocus={stableOnFocusInput}
|
||||
currentUser={currentUser}
|
||||
otherUser={otherUser}
|
||||
getSenderInfo={getSenderInfo}
|
||||
@@ -587,7 +600,7 @@ export const ChatScreen: React.FC<ChatScreenProps> = (props) => {
|
||||
onInsertEmoji={handleInsertEmoji}
|
||||
onInsertSticker={handleSendSticker}
|
||||
onClose={closePanel}
|
||||
onFocusInput={() => textInputRef.current?.focus()}
|
||||
onFocusInput={stableFocusTextInput}
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
@@ -657,10 +670,7 @@ export const ChatScreen: React.FC<ChatScreenProps> = (props) => {
|
||||
{/* 图片查看器 */}
|
||||
<ImageGallery
|
||||
visible={showImageViewer}
|
||||
images={chatImages.map(img => ({
|
||||
id: img.id || img.url || String(Math.random()),
|
||||
url: img.url || img.uri || ''
|
||||
}))}
|
||||
images={stableGalleryImages}
|
||||
initialIndex={selectedImageIndex}
|
||||
onClose={handleCloseImageViewer}
|
||||
enableSave
|
||||
|
||||
@@ -152,6 +152,11 @@ export const MessageListScreen: React.FC = () => {
|
||||
// 系统通知显示状态 - 用于在移动端显示通知页面
|
||||
const [showNotifications, setShowNotifications] = useState(false);
|
||||
|
||||
// Stable callbacks to avoid inline arrows passed to child components
|
||||
const stableCloseNotifications = useCallback(() => setShowNotifications(false), []);
|
||||
const stableClearSelectedConversation = useCallback(() => setSelectedConversation(null), []);
|
||||
const stableCloseScanner = useCallback(() => setScannerVisible(false), []);
|
||||
|
||||
// 系统消息会话对象(用于选中状态)
|
||||
const systemMessageConversation: ConversationResponse = useMemo(() => ({
|
||||
id: SYSTEM_MESSAGE_CHANNEL_ID,
|
||||
@@ -197,14 +202,11 @@ export const MessageListScreen: React.FC = () => {
|
||||
return FLOATING_TAB_BAR_HEIGHT + insets.bottom + spacing.md;
|
||||
}, [isWideScreen, insets.bottom]);
|
||||
|
||||
// 【新架构】页面获得焦点时初始化MessageManager
|
||||
// 初始化MessageManager(仅首次),焦点时轻量刷新会话列表
|
||||
useEffect(() => {
|
||||
if (isFocused) {
|
||||
messageManager.initialize();
|
||||
}
|
||||
}, [isFocused]);
|
||||
messageManager.initialize();
|
||||
}, []);
|
||||
|
||||
// 【新架构】使用focus刷新hook,从ChatScreen返回时自动刷新未读数
|
||||
useMessageListRefresh();
|
||||
|
||||
// 同步未读数到userStore(用于TabBar角标显示)
|
||||
@@ -840,7 +842,7 @@ export const MessageListScreen: React.FC = () => {
|
||||
<View style={styles.chatArea}>
|
||||
{showNotifications ? (
|
||||
// 显示系统通知页面
|
||||
<NotificationsScreen onBack={() => setShowNotifications(false)} />
|
||||
<NotificationsScreen onBack={stableCloseNotifications} />
|
||||
) : selectedConversation ? (
|
||||
// 显示选中会话的聊天内容
|
||||
<ChatScreen
|
||||
@@ -849,7 +851,7 @@ export const MessageListScreen: React.FC = () => {
|
||||
embeddedIsGroupChat={selectedConversation.type === 'group'}
|
||||
embeddedGroupId={selectedConversation.group ? String(selectedConversation.group.id) : undefined}
|
||||
embeddedGroupName={selectedConversation.group?.name}
|
||||
onEmbeddedBack={() => setSelectedConversation(null)}
|
||||
onEmbeddedBack={stableClearSelectedConversation}
|
||||
/>
|
||||
) : (
|
||||
// 默认占位符
|
||||
@@ -871,7 +873,7 @@ export const MessageListScreen: React.FC = () => {
|
||||
<SafeAreaView style={styles.container} edges={['top', 'bottom']}>
|
||||
{showNotifications ? (
|
||||
// 显示系统通知页面,传入 onBack 回调
|
||||
<NotificationsScreen onBack={() => setShowNotifications(false)} />
|
||||
<NotificationsScreen onBack={stableCloseNotifications} />
|
||||
) : isSearchMode ? (
|
||||
renderSearchMode()
|
||||
) : isWideScreen ? (
|
||||
@@ -882,7 +884,7 @@ export const MessageListScreen: React.FC = () => {
|
||||
renderConversationList()
|
||||
)}
|
||||
{renderActionMenu()}
|
||||
<QRCodeScanner visible={scannerVisible} onClose={() => setScannerVisible(false)} />
|
||||
<QRCodeScanner visible={scannerVisible} onClose={stableCloseScanner} />
|
||||
</SafeAreaView>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user