diff --git a/src/components/HomeNavbar/index.tsx b/src/components/HomeNavbar/index.tsx index 6e96650..ff5cc8e 100644 --- a/src/components/HomeNavbar/index.tsx +++ b/src/components/HomeNavbar/index.tsx @@ -231,13 +231,13 @@ const HomeNavbar = (props: IProps) => { // 处理城市切换(仅刷新数据,不保存缓存) const handleCityChangeWithoutCache = async () => { - // 切换城市后,同时更新两个列表接口获取数据 + // 切换城市后,同时更新两个列表接口获取数据,传入当前的 area if (refreshBothLists) { - await refreshBothLists(); + await refreshBothLists(area); } - // 更新球局数量 + // 更新球局数量,传入当前的 area,确保接口请求的地址与界面显示一致 if (fetchGetGamesCount) { - await fetchGetGamesCount(); + await fetchGetGamesCount(area); } }; diff --git a/src/components/NTRPTestEntryCard/index.tsx b/src/components/NTRPTestEntryCard/index.tsx index f4ea19b..ecd397d 100644 --- a/src/components/NTRPTestEntryCard/index.tsx +++ b/src/components/NTRPTestEntryCard/index.tsx @@ -2,9 +2,9 @@ import React, { useState, useEffect, useCallback, memo } from "react"; import { View, Image, Text } from "@tarojs/components"; import { requireLoginWithPhone } from "@/utils/helper"; import Taro from "@tarojs/taro"; -import { useUserInfo, useUserActions } from "@/store/userStore"; +import { useUserInfo, useUserActions, useLastTestResult } from "@/store/userStore"; // import { getCurrentFullPath } from "@/utils"; -import evaluateService, { StageType } from "@/services/evaluateService"; +import { StageType } from "@/services/evaluateService"; import { waitForAuthInit } from "@/utils/authInit"; import DocCopy from "@/static/ntrp/ntrp_doc_copy.svg"; import ArrowRight from "@/static/ntrp/ntrp_arrow_right_color.svg"; @@ -19,15 +19,16 @@ function NTRPTestEntryCard(props: { type: EvaluateScene; evaluateCallback?: EvaluateCallback; }) { - const [testFlag, setTestFlag] = useState(false); - const [hasTestInLastMonth, setHasTestInLastMonth] = useState(false); const { type, evaluateCallback } = props; const userInfo = useUserInfo(); const { setCallback } = useEvaluate(); - const { fetchUserInfo } = useUserActions(); + const { fetchUserInfo, fetchLastTestResult } = useUserActions(); + // 使用全局状态中的测试结果,避免重复调用接口 + const lastTestResult = useLastTestResult(); console.log(userInfo); + // 从全局状态中获取测试结果,如果不存在则调用接口(使用请求锁避免重复调用) useEffect(() => { const init = async () => { // 先等待静默登录完成 @@ -36,15 +37,17 @@ function NTRPTestEntryCard(props: { if (!userInfo.id) { await fetchUserInfo(); } - // 获取测试结果 - const res = await evaluateService.getLastResult(); - if (res.code === 0) { - setTestFlag(res.data.has_test_record); - setHasTestInLastMonth(res.data.has_test_in_last_month); + // 如果全局状态中没有测试结果,则调用接口(使用请求锁,多个组件同时调用时只会请求一次) + if (!lastTestResult) { + await fetchLastTestResult(); } }; init(); - }, [userInfo]); + }, [userInfo.id, fetchUserInfo, fetchLastTestResult, lastTestResult]); + + // 从全局状态中计算标志位 + const testFlag = lastTestResult?.has_test_record || false; + const hasTestInLastMonth = lastTestResult?.has_test_in_last_month || false; const handleTest = useCallback( function () { diff --git a/src/components/UserInfo/index.tsx b/src/components/UserInfo/index.tsx index 507ab3e..fb6f2c1 100644 --- a/src/components/UserInfo/index.tsx +++ b/src/components/UserInfo/index.tsx @@ -6,7 +6,7 @@ import "./index.scss"; import { EditModal } from "@/components"; import { UserService, PickerOption } from "@/services/userService"; import { PopupPicker } from "@/components/Picker/index"; -import { useUserActions, useNicknameChangeStatus } from "@/store/userStore"; +import { useUserActions, useNicknameChangeStatus, useLastTestResult } from "@/store/userStore"; import { UserInfoType } from "@/services/userService"; import { useCities, @@ -15,7 +15,6 @@ import { } from "@/store/pickerOptionsStore"; import { formatNtrpDisplay } from "@/utils/helper"; import { useGlobalState } from "@/store/global"; -import evaluateService from "@/services/evaluateService"; // 用户信息接口 // export interface UserInfo { @@ -83,9 +82,10 @@ const UserInfoCardComponent: React.FC = ({ }) => { const nickname_change_status = useNicknameChangeStatus(); const { setShowGuideBar } = useGlobalState(); - const { updateUserInfo, updateNickname } = useUserActions(); + const { updateUserInfo, updateNickname, fetchLastTestResult } = useUserActions(); const ntrpLevels = useNtrpLevels(); - const [ntrpTested, setNtrpTested] = useState(false); + // 使用全局状态中的测试结果,避免重复调用接口 + const lastTestResult = useLastTestResult(); // 使用 useRef 记录上一次的 user_info,只在真正变化时打印 const prevUserInfoRef = useRef>(); @@ -98,15 +98,14 @@ const UserInfoCardComponent: React.FC = ({ console.log("UserInfoCard 用户信息变化:", user_info); prevUserInfoRef.current = user_info; } - const getLastResult = async () => { - // 获取测试结果 - const res = await evaluateService.getLastResult(); - if (res.code === 0) { - setNtrpTested(res.data.has_test_in_last_month); - } - }; - getLastResult(); - }, [user_info]); + // 如果全局状态中没有测试结果,则调用接口(使用请求锁,多个组件同时调用时只会请求一次) + if (!lastTestResult && user_info?.id) { + fetchLastTestResult(); + } + }, [user_info?.id, lastTestResult, fetchLastTestResult]); + + // 从全局状态中获取测试状态 + const ntrpTested = lastTestResult?.has_test_in_last_month || false; // 编辑个人简介弹窗状态 const [edit_modal_visible, setEditModalVisible] = useState(false); diff --git a/src/container/listContainer/index.tsx b/src/container/listContainer/index.tsx index 46abaab..ec5a412 100644 --- a/src/container/listContainer/index.tsx +++ b/src/container/listContainer/index.tsx @@ -3,11 +3,9 @@ import ListCard from "@/components/ListCard"; import ListLoadError from "@/components/ListLoadError"; import ListCardSkeleton from "@/components/ListCardSkeleton"; import { useReachBottom } from "@tarojs/taro"; -import { useUserInfo, useUserActions } from "@/store/userStore"; -import { setStorage, getStorage } from "@/store/storage"; +import { useUserInfo, useUserActions, useLastTestResult } from "@/store/userStore"; import { NTRPTestEntryCard } from "@/components"; import { EvaluateScene } from "@/store/evaluateStore"; -import evaluateService from "@/services/evaluateService"; import { waitForAuthInit } from "@/utils/authInit"; import "./index.scss"; import { useRef, useEffect, useState, useMemo } from "react"; @@ -40,10 +38,11 @@ const ListContainer = (props) => { const [showNumber, setShowNumber] = useState(0); const [showSkeleton, setShowSkeleton] = useState(false); - const [hasTestInLastMonth, setHasTestInLastMonth] = useState(false); const userInfo = useUserInfo(); - const { fetchUserInfo } = useUserActions(); + const { fetchUserInfo, fetchLastTestResult } = useUserActions(); + // 使用全局状态中的测试结果,避免重复调用接口 + const lastTestResult = useLastTestResult(); useReachBottom(() => { // 加载更多方法 @@ -100,17 +99,21 @@ const ListContainer = (props) => { // 先等待静默登录完成 await waitForAuthInit(); // 然后再获取用户信息 - if (!userInfo.id) { + const userInfoId = userInfo && 'id' in userInfo ? userInfo.id : null; + if (!userInfoId) { await fetchUserInfo(); + return; // 等待下一次 useEffect 触发(此时 userInfo.id 已有值) } - // 获取测试结果 - const res = await evaluateService.getLastResult(); - if (res.code === 0) { - setHasTestInLastMonth(res.data.has_test_in_last_month); + // 如果全局状态中没有测试结果,则调用接口(使用请求锁,多个组件同时调用时只会请求一次) + if (!lastTestResult) { + await fetchLastTestResult(); } }; init(); - }, [evaluateFlag, userInfo.id]); + }, [evaluateFlag, userInfo, lastTestResult, fetchLastTestResult]); + + // 从全局状态中获取测试状态 + const hasTestInLastMonth = lastTestResult?.has_test_in_last_month || false; if (error) { return ; diff --git a/src/main_pages/components/ListPageContent.tsx b/src/main_pages/components/ListPageContent.tsx index cc97209..b23c60e 100644 --- a/src/main_pages/components/ListPageContent.tsx +++ b/src/main_pages/components/ListPageContent.tsx @@ -13,6 +13,7 @@ import { useDictionaryStore } from "@/store/dictionaryStore"; import { saveImage, navigateTo } from "@/utils"; export interface ListPageContentProps { + isActive?: boolean; // 是否处于激活状态(当前显示的页面) onNavStateChange?: (state: { isShowInputCustomerNavBar?: boolean; isDistanceFilterVisible?: boolean; @@ -26,6 +27,7 @@ export interface ListPageContentProps { } const ListPageContent: React.FC = ({ + isActive = true, onNavStateChange, onScrollToTop: _onScrollToTop, scrollToTopTrigger, @@ -53,6 +55,7 @@ const ListPageContent: React.FC = ({ initialFilterSearch, loadMoreMatches, fetchGetGamesCount, + refreshBothLists, updateDistanceQuickFilter, getCities, getCityQrCode, @@ -86,6 +89,9 @@ const ListPageContent: React.FC = ({ const [showSearchBar, setShowSearchBar] = useState(true); const [scrollTop, setScrollTop] = useState(0); const [refreshing, setRefreshing] = useState(false); + // 记录上一次加载数据时的城市,用于检测城市变化 + const lastLoadedAreaRef = useRef<[string, string] | null>(null); + const prevIsActiveRef = useRef(isActive); // 处理距离筛选显示/隐藏 const handleDistanceFilterVisibleChange = useCallback( @@ -200,11 +206,80 @@ const ListPageContent: React.FC = ({ getCityQrCode(); getDistricts(); // 新增:获取行政区列表 - getLocation().catch((error) => { - console.error('获取位置信息失败:', error); - }); + // 只有当页面激活时才加载位置和列表数据 + if (isActive) { + getLocation().catch((error) => { + console.error('获取位置信息失败:', error); + }); + } + }, [isActive]); - }, []); + // 当页面从非激活状态切换为激活状态时,检查城市是否变化,如果变化则重新加载数据 + useEffect(() => { + // 如果从非激活状态变为激活状态(切回列表页) + if (isActive && !prevIsActiveRef.current) { + const currentArea = area; + const lastArea = lastLoadedAreaRef.current; + + // 检查城市是否发生变化(比较省份) + const currentProvince = currentArea?.[1] || ""; + const lastProvince = lastArea?.[1] || ""; + + // 如果城市发生变化,或者地址存在但不一致,需要重新加载数据 + // 注意:即使 lastArea 为空,只要 currentArea 存在,也应该加载数据 + if (currentProvince && (currentProvince !== lastProvince || !lastArea)) { + console.log("切回列表页,检测到地址变化或不一致,重新加载数据:", { + lastArea, + currentArea, + lastProvince, + currentProvince, + }); + + // 地址发生变化或不一致,重新加载数据和球局数量 + const promises: Promise[] = []; + if (refreshBothLists) { + promises.push(refreshBothLists(currentArea)); + } + if (fetchGetGamesCount) { + promises.push(fetchGetGamesCount(currentArea)); + } + Promise.all(promises).then(() => { + // 数据加载完成后,更新记录的城市(记录为上一次在列表页加载数据时的城市) + if (currentArea) { + lastLoadedAreaRef.current = [...currentArea] as [string, string]; + } + }).catch((error) => { + console.error("重新加载数据失败:", error); + }); + } + } + + // 如果是首次加载且列表页激活,记录当前城市(用于后续比较) + if (isActive && !lastLoadedAreaRef.current && area) { + lastLoadedAreaRef.current = [...area] as [string, string]; + } + + // 更新上一次的激活状态 + prevIsActiveRef.current = isActive; + }, [isActive, area, refreshBothLists, fetchGetGamesCount]); + + // 监听城市变化(在列表页激活状态下),当城市切换后立即更新记录 + // 注意:这个 useEffect 用于处理在列表页激活状态下切换城市的情况 + // 当用户在列表页切换城市时,HomeNavbar 的 handleCityChange 已经会调用 refreshBothLists + // 这里只需要同步更新 lastLoadedAreaRef,确保后续检测逻辑正确 + useEffect(() => { + // 如果页面激活且城市发生变化(用户在列表页切换了城市) + if (isActive && area) { + const currentProvince = area[1] || ""; + const lastProvince = lastLoadedAreaRef.current?.[1] || ""; + + // 如果城市发生变化,立即更新记录(因为 refreshBothLists 已经在 HomeNavbar 中调用) + if (currentProvince && currentProvince !== lastProvince) { + // 立即更新记录,确保地址显示和使用的地址一致 + lastLoadedAreaRef.current = [...area] as [string, string]; + } + } + }, [isActive, area]); useEffect(() => { if (pageOption?.page === 1 && matches?.length > 0) { @@ -237,8 +312,13 @@ const ListPageContent: React.FC = ({ console.error("更新用户位置失败:", error); } } - fetchGetGamesCount(); - getMatchesData(); + // 传入当前的 area,确保接口请求的地址与界面显示一致 + await fetchGetGamesCount(area); + await getMatchesData(); + // 初始数据加载完成后,记录当前城市 + if (area && isActive) { + lastLoadedAreaRef.current = [...area] as [string, string]; + } return location; }; diff --git a/src/main_pages/components/MessagePageContent.tsx b/src/main_pages/components/MessagePageContent.tsx index 73140cd..8f79305 100644 --- a/src/main_pages/components/MessagePageContent.tsx +++ b/src/main_pages/components/MessagePageContent.tsx @@ -1,4 +1,5 @@ import { useState, useEffect } from "react"; +import React from "react"; import { View, Text, Image, ScrollView } from "@tarojs/components"; import { EmptyState } from "@/components"; import SubscribeNotificationTip from "@/components/SubscribeNotificationTip"; @@ -25,7 +26,11 @@ interface MessageItem { type MessageCategory = "comment" | "follow"; -const MessagePageContent = () => { +interface MessagePageContentProps { + isActive?: boolean; +} + +const MessagePageContent: React.FC = ({ isActive = true }) => { const { statusNavbarHeightInfo } = useGlobalState() || {}; const { totalHeight = 98 } = statusNavbarHeightInfo || {}; @@ -34,6 +39,7 @@ const MessagePageContent = () => { const [loading, setLoading] = useState(false); const [reachedBottom, setReachedBottom] = useState(false); const [refreshing, setRefreshing] = useState(false); + const [hasLoaded, setHasLoaded] = useState(false); // 记录是否已经加载过数据 // 从 store 获取红点信息 const reddotInfo = useReddotInfo(); @@ -58,10 +64,14 @@ const MessagePageContent = () => { } }; + // 只有当页面激活且未加载过数据时才加载接口 useEffect(() => { - getNoticeList(); - fetchReddotInfo(); - }, []); + if (isActive && !hasLoaded) { + getNoticeList(); + fetchReddotInfo(); + setHasLoaded(true); + } + }, [isActive, hasLoaded]); const filteredMessages = messageList; diff --git a/src/main_pages/components/MyselfPageContent.tsx b/src/main_pages/components/MyselfPageContent.tsx index 27ff916..b6103ff 100644 --- a/src/main_pages/components/MyselfPageContent.tsx +++ b/src/main_pages/components/MyselfPageContent.tsx @@ -8,14 +8,19 @@ import ListContainer from "@/container/listContainer"; import { TennisMatch } from "@/../types/list/types"; import { NTRPTestEntryCard } from "@/components"; import { EvaluateScene } from "@/store/evaluateStore"; -import { useUserInfo } from "@/store/userStore"; +import { useUserInfo, useUserActions } from "@/store/userStore"; import { usePickerOption } from "@/store/pickerOptionsStore"; import { useGlobalState } from "@/store/global"; -const MyselfPageContent: React.FC = () => { +interface MyselfPageContentProps { + isActive?: boolean; +} + +const MyselfPageContent: React.FC = ({ isActive = true }) => { const pickerOption = usePickerOption(); const { statusNavbarHeightInfo } = useGlobalState() || {}; const { totalHeight = 98 } = statusNavbarHeightInfo || {}; + const { fetchUserInfo } = useUserActions(); const instance = (Taro as any).getCurrentInstance(); const user_id = instance.router?.params?.userid || ""; @@ -29,6 +34,7 @@ const MyselfPageContent: React.FC = () => { const [active_tab, setActiveTab] = useState<"hosted" | "participated">( "hosted" ); + const [hasLoaded, setHasLoaded] = useState(false); // 记录是否已经加载过数据 const [collapseProfile, setCollapseProfile] = useState(false); @@ -37,6 +43,16 @@ const MyselfPageContent: React.FC = () => { pickerOption.getProfessions(); }, []); + // 当页面激活时,确保用户信息已加载 + useEffect(() => { + if (isActive) { + // 如果用户信息不存在或没有 id,则加载用户信息 + if (!user_info || !("id" in user_info) || !user_info.id) { + fetchUserInfo(); + } + } + }, [isActive, user_info, fetchUserInfo]); + const { useDidShow } = Taro as any; useDidShow(() => { // 确保从编辑页面返回时刷新数据 @@ -92,11 +108,20 @@ const MyselfPageContent: React.FC = () => { } }, [active_tab, user_info, classifyGameRecords]); + // 只有当页面激活且未加载过数据时才加载接口 useEffect(() => { - if (!loading) { + if (isActive && !hasLoaded && !loading && user_info && "id" in user_info) { + load_game_data(); + setHasLoaded(true); + } + }, [isActive, hasLoaded, loading, load_game_data, user_info]); + + // 当 active_tab 切换时,如果页面已激活,重新加载数据 + useEffect(() => { + if (isActive && hasLoaded && !loading && user_info && "id" in user_info) { load_game_data(); } - }, [loading, load_game_data]); + }, [active_tab, isActive, hasLoaded, loading, load_game_data, user_info]); const handle_follow = async () => { try { diff --git a/src/main_pages/index.tsx b/src/main_pages/index.tsx index 752276c..65943ed 100644 --- a/src/main_pages/index.tsx +++ b/src/main_pages/index.tsx @@ -230,6 +230,7 @@ const MainPage: React.FC = () => { className={`tab-content ${currentTab === "list" ? "active" : ""}`} > { - + {/* 我的页内容 */} - + {/* 底部导航栏 */} diff --git a/src/store/listStore.ts b/src/store/listStore.ts index afe3095..ad2c6fd 100644 --- a/src/store/listStore.ts +++ b/src/store/listStore.ts @@ -193,12 +193,11 @@ export const useListStore = create()((set, get) => ({ const distanceQuickFilter = currentPageState?.distanceQuickFilter || {}; const { distanceFilter, order, district } = distanceQuickFilter || {}; - // 优先使用 overrideArea(切换城市时传入),其次使用 area 状态中的省份,最后使用用户信息中的 + // 优先使用 overrideArea(切换城市时传入),其次使用 area 状态 + // area 会在 userLastLocationProvince 更新时自动同步,所以这里直接使用 area 即可 const areaProvince = overrideArea?.at(1) || state.area?.at(1) || ""; const userInfo = useUser.getState().user as any; - const userLastLocationProvince = userInfo?.last_location_province || ""; - // 优先使用切换后的城市,如果没有切换过则使用用户信息中的 - const last_location_province = areaProvince || userLastLocationProvince; + const last_location_province = areaProvince; const last_location_city = userInfo?.last_location_city || ""; // city 参数逻辑: @@ -530,7 +529,8 @@ export const useListStore = create()((set, get) => ({ // 使用 Promise.resolve 确保状态更新后再调用接口 Promise.resolve().then(() => { const freshState = get(); // 重新获取最新状态 - freshState.fetchGetGamesCount(); + // 传入当前的 area,确保接口请求的地址与界面显示一致 + freshState.fetchGetGamesCount(freshState.area); }); }, @@ -551,7 +551,8 @@ export const useListStore = create()((set, get) => ({ Promise.resolve().then(() => { const freshState = get(); // 重新获取最新状态 freshState.getMatchesData(); - freshState.fetchGetGamesCount(); + // 传入当前的 area,确保接口请求的地址与界面显示一致 + freshState.fetchGetGamesCount(freshState.area); }); }, diff --git a/src/store/userStore.ts b/src/store/userStore.ts index def90fe..1f5d3a9 100644 --- a/src/store/userStore.ts +++ b/src/store/userStore.ts @@ -7,6 +7,8 @@ import { NicknameChangeStatus, updateNickname as updateNicknameApi, } from "@/services/userService"; +import evaluateService, { LastTimeTestResult } from "@/services/evaluateService"; +import { useListStore } from "./listStore"; export interface UserState { user: UserInfoType | {}; @@ -15,35 +17,11 @@ export interface UserState { nicknameChangeStatus: Partial; checkNicknameChangeStatus: () => void; updateNickname: (nickname: string) => void; + // NTRP 测试结果缓存 + lastTestResult: LastTimeTestResult | null; + fetchLastTestResult: () => Promise; } -// 请求锁,防止重复请求 -let fetchingUserInfo = false; -let fetchUserInfoPromise: Promise | null = null; - -const fetchUserInfoWithLock = async (set) => { - // 如果正在请求,直接返回现有的 Promise - if (fetchingUserInfo && fetchUserInfoPromise) { - return fetchUserInfoPromise; - } - - fetchingUserInfo = true; - fetchUserInfoPromise = (async () => { - try { - const res = await fetchUserProfile(); - set({ user: res.data }); - return res.data; - } catch (error) { - console.error("获取用户信息失败:", error); - return undefined; - } finally { - fetchingUserInfo = false; - fetchUserInfoPromise = null; - } - })(); - - return fetchUserInfoPromise; -}; const getTimeNextDate = (time: string) => { const date = new Date(time); @@ -56,15 +34,50 @@ const getTimeNextDate = (time: string) => { export const useUser = create()((set) => ({ user: {}, - fetchUserInfo: () => fetchUserInfoWithLock(set), + fetchUserInfo: async () => { + try { + const res = await fetchUserProfile(); + const userData = res.data; + set({ user: userData }); + + // 当 userLastLocationProvince 更新时,同步更新 area + if (userData?.last_location_province) { + const listStore = useListStore.getState(); + const currentArea = listStore.area; + // 只有当 area 不存在或与 userLastLocationProvince 不一致时才更新 + if (!currentArea || currentArea[1] !== userData.last_location_province) { + const newArea: [string, string] = ["中国", userData.last_location_province]; + listStore.updateArea(newArea); + } + } + + return userData; + } catch (error) { + console.error("获取用户信息失败:", error); + return undefined; + } + }, updateUserInfo: async (userInfo: Partial) => { try { // 先更新后端 await updateUserProfile(userInfo); // 然后立即更新本地状态(乐观更新) - set((state) => ({ - user: { ...state.user, ...userInfo }, - })); + set((state) => { + const newUser = { ...state.user, ...userInfo }; + + // 当 userLastLocationProvince 更新时,同步更新 area + if (userInfo.last_location_province) { + const listStore = useListStore.getState(); + const currentArea = listStore.area; + // 只有当 area 不存在或与 userLastLocationProvince 不一致时才更新 + if (!currentArea || currentArea[1] !== userInfo.last_location_province) { + const newArea: [string, string] = ["中国", userInfo.last_location_province]; + listStore.updateArea(newArea); + } + } + + return { user: newUser }; + }); // 不再每次都重新获取完整用户信息,减少请求次数 // 只有在更新头像等需要服务器返回新URL的字段时才需要重新获取 // 如果需要确保数据一致性,可以在特定场景下手动调用 fetchUserInfo @@ -99,6 +112,21 @@ export const useUser = create()((set) => ({ console.error("更新用户昵称失败:", error); } }, + // NTRP 测试结果缓存 + lastTestResult: null, + fetchLastTestResult: async () => { + try { + const res = await evaluateService.getLastResult(); + if (res.code === 0) { + set({ lastTestResult: res.data }); + return res.data; + } + return null; + } catch (error) { + console.error("获取NTRP测试结果失败:", error); + return null; + } + }, })); export const useUserInfo = () => useUser((state) => state.user); @@ -111,4 +139,7 @@ export const useUserActions = () => updateUserInfo: state.updateUserInfo, checkNicknameChangeStatus: state.checkNicknameChangeStatus, updateNickname: state.updateNickname, + fetchLastTestResult: state.fetchLastTestResult, })); + +export const useLastTestResult = () => useUser((state) => state.lastTestResult);