Просмотр исходного кода

♻️ refactor(profile): migrate to React Query for data management

- 移除useEffect数据加载逻辑,使用useQuery实现用户档案数据获取
- 将手动状态管理替换为React Query的useMutation处理API请求
- 实现queryClient缓存失效机制,确保数据更新后自动刷新
- 合并重复的loading状态,使用React Query内置的isLoading状态
- 优化错误处理,统一使用React Query的error状态管理

✨ feat(profile): improve data loading and submission experience

- 添加profileError状态处理,增强错误捕获能力
- 实现表单数据自动填充的useEffect依赖优化
- 优化数据提交流程,简化异步操作代码结构
- 增强组件响应式,提升用户交互体验

🔧 chore(import): optimize import statements

- 移除未使用的useEffect和useState导入
- 添加React Query相关钩子导入
- 调整导入顺序,优化代码组织结构
yourname 7 месяцев назад
Родитель
Сommit
d522a4a046
1 измененных файлов с 112 добавлено и 100 удалено
  1. 112 100
      src/client/mobile/pages/ProfileEditPage.tsx

+ 112 - 100
src/client/mobile/pages/ProfileEditPage.tsx

@@ -1,4 +1,4 @@
-import React, { useState, useEffect } from 'react';
+import React, { useState } from 'react';
 import { useAuth } from '../hooks/AuthProvider';
 import { useNavigate } from 'react-router-dom';
 import { silverUserProfileClient, userClient } from '@/client/api';
@@ -7,6 +7,7 @@ import { zodResolver } from '@hookform/resolvers/zod';
 import { z } from 'zod';
 import { toast } from 'sonner';
 import { InferRequestType, InferResponseType } from 'hono/client';
+import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
 
 // 导入 shadcn 组件
 import { Button } from '@/client/components/ui/button';
@@ -46,9 +47,7 @@ type ProfileFormData = z.infer<typeof profileFormSchema>;
 const ProfileEditPage: React.FC = () => {
   const { user } = useAuth();
   const navigate = useNavigate();
-  const [loading, setLoading] = useState(false);
-  const [profile, setProfile] = useState<ProfileResponse | null>(null);
-  const [isLoading, setIsLoading] = useState(true);
+  const queryClient = useQueryClient();
   const [avatarFileId, setAvatarFileId] = useState<number | null>(null);
 
   // 初始化表单
@@ -68,64 +67,101 @@ const ProfileEditPage: React.FC = () => {
     },
   });
 
-  useEffect(() => {
-    if (user) {
-      loadProfile();
-    }
-  }, [user]);
-
-  const loadProfile = async () => {
-    if (!user) return;
-
-    try {
-      setIsLoading(true);
+  // 使用 React Query 获取用户档案数据
+  const { data: profileData, isLoading: isLoadingProfile, error: profileError } = useQuery({
+    queryKey: ['userProfile', user?.id],
+    queryFn: async () => {
+      if (!user) throw new Error('用户未登录');
+      
       const response = await silverUserProfileClient.$get({
         query: { filters: JSON.stringify({ userId: user.id }) }
       });
 
-      if (response.status === 200) {
-        const data = await response.json();
-        if (data.data && data.data.length > 0) {
-          const profileData = data.data[0];
-          setProfile(profileData);
-          setAvatarFileId(profileData.avatarFileId || null);
-
-          // 填充表单数据
-          form.reset({
-            username: user.username || '',
-            email: user.email || '',
-            phone: profileData.phone || user.phone || '',
-            realName: profileData.realName || '',
-            age: profileData.age || 18,
-            gender: profileData.gender || 1,
-            organization: profileData.organization || '',
-            personalIntro: profileData.personalIntro || '',
-            personalSkills: profileData.personalSkills || '',
-            personalExperience: profileData.personalExperience || '',
-          });
-        } else {
-          // 新用户,使用现有用户信息填充
-          form.reset({
-            username: user.username || '',
-            email: user.email || '',
-            phone: user.phone || '',
-            realName: '',
-            age: 18,
-            gender: 1,
-            organization: '',
-            personalIntro: '',
-            personalSkills: '',
-            personalExperience: '',
-          });
+      if (response.status !== 200) {
+        throw new Error('获取档案失败');
+      }
+
+      const data = await response.json();
+      return data.data?.[0] || null;
+    },
+    enabled: !!user,
+  });
+
+  // 使用 React Query 更新用户信息
+  const updateUserMutation = useMutation({
+    mutationFn: async (data: UpdateUserRequest) => {
+      if (!user) throw new Error('用户未登录');
+      
+      const response = await userClient[':id'].$put({
+        param: { id: user.id },
+        json: data,
+      });
+
+      if (response.status !== 200) {
+        throw new Error('更新用户信息失败');
+      }
+
+      return response.json();
+    },
+    onSuccess: () => {
+      queryClient.invalidateQueries({ queryKey: ['userProfile', user?.id] });
+    },
+  });
+
+  // 使用 React Query 创建/更新档案
+  const updateProfileMutation = useMutation({
+    mutationFn: async (data: CreateProfileRequest | UpdateProfileRequest) => {
+      if (!user) throw new Error('用户未登录');
+      
+      const isUpdate = profileData?.id;
+      
+      if (isUpdate) {
+        const response = await silverUserProfileClient[':id'].$put({
+          param: { id: profileData.id },
+          json: data as UpdateProfileRequest,
+        });
+        
+        if (response.status !== 200) {
+          throw new Error('更新档案失败');
         }
+        
+        return response.json();
+      } else {
+        const response = await silverUserProfileClient.$post({
+          json: data as CreateProfileRequest,
+        });
+        
+        if (response.status !== 201) {
+          throw new Error('创建档案失败');
+        }
+        
+        return response.json();
       }
-    } catch (error) {
-      console.error('加载个人档案失败:', error);
-      toast.error('加载个人档案失败');
-    } finally {
-      setIsLoading(false);
+    },
+    onSuccess: () => {
+      queryClient.invalidateQueries({ queryKey: ['userProfile', user?.id] });
+    },
+  });
+
+  // 根据获取的数据填充表单
+  React.useEffect(() => {
+    if (user && profileData !== undefined) {
+      setAvatarFileId(profileData?.avatarFileId || null);
+      
+      form.reset({
+        username: user.username || '',
+        email: user.email || '',
+        phone: profileData?.phone || user.phone || '',
+        realName: profileData?.realName || '',
+        age: profileData?.age || 18,
+        gender: profileData?.gender || 1,
+        organization: profileData?.organization || '',
+        personalIntro: profileData?.personalIntro || '',
+        personalSkills: profileData?.personalSkills || '',
+        personalExperience: profileData?.personalExperience || '',
+      });
     }
-  };
+  }, [user, profileData, form]);
 
   const handleAvatarChange = (fileId: number) => {
     setAvatarFileId(fileId);
@@ -134,7 +170,6 @@ const ProfileEditPage: React.FC = () => {
   const onSubmit = async (data: ProfileFormData) => {
     if (!user) return;
 
-    setLoading(true);
     try {
       // 更新用户信息
       const userUpdateData: UpdateUserRequest = {
@@ -143,53 +178,30 @@ const ProfileEditPage: React.FC = () => {
         phone: data.phone,
       };
 
-      const userUpdateResponse = await userClient[':id'].$put({
-        param: { id: user.id },
-        json: userUpdateData,
-      });
+      await updateUserMutation.mutateAsync(userUpdateData);
 
-      if (userUpdateResponse.status === 200) {
-        // 更新银龄档案
-        const profileData: CreateProfileRequest | UpdateProfileRequest = {
-          userId: user.id,
-          realName: data.realName,
-          age: data.age,
-          gender: data.gender as 1 | 2 | 3,
-          phone: data.phone,
-          email: data.email,
-          avatarFileId: avatarFileId || undefined,
-          organization: data.organization,
-          personalIntro: data.personalIntro,
-          personalSkills: data.personalSkills,
-          personalExperience: data.personalExperience,
-        };
-
-        let response;
-        if (profile?.id) {
-          response = await silverUserProfileClient[':id'].$put({
-            param: { id: profile.id },
-            json: profileData as UpdateProfileRequest,
-          });
-        } else {
-          response = await silverUserProfileClient.$post({
-            json: profileData as CreateProfileRequest,
-          });
-        }
+      // 更新银龄档案
+      const profileData: CreateProfileRequest | UpdateProfileRequest = {
+        userId: user.id,
+        realName: data.realName,
+        age: data.age,
+        gender: data.gender as 1 | 2 | 3,
+        phone: data.phone,
+        email: data.email,
+        avatarFileId: avatarFileId || undefined,
+        organization: data.organization,
+        personalIntro: data.personalIntro,
+        personalSkills: data.personalSkills,
+        personalExperience: data.personalExperience,
+      };
 
-        if (response.status === 200 || response.status === 201) {
-          toast.success('个人信息更新成功');
-          navigate('/profile');
-        } else {
-          throw new Error('更新个人档案失败');
-        }
-      } else {
-        throw new Error('更新用户信息失败');
-      }
+      await updateProfileMutation.mutateAsync(profileData);
+      
+      toast.success('个人信息更新成功');
+      navigate('/profile');
     } catch (error) {
       console.error('更新失败:', error);
       toast.error('更新失败,请重试');
-    } finally {
-      setLoading(false);
     }
   };
 
@@ -210,7 +222,7 @@ const ProfileEditPage: React.FC = () => {
     );
   }
 
-  if (isLoading) {
+  if (isLoadingProfile) {
     return (
       <div className="min-h-screen bg-gray-50">
         <div className="bg-white border-b">
@@ -262,7 +274,7 @@ const ProfileEditPage: React.FC = () => {
               <CardContent>
                 <div className="flex items-center space-x-4">
                   <AvatarUpload
-                    currentAvatar={avatarFileId ? profile?.avatarFile?.fullUrl : ''}
+                    currentAvatar={avatarFileId ? profileData?.avatarFile?.fullUrl : ''}
                     size={80}
                     onUploadSuccess={handleAvatarChange}
                   />