React Hooks封装Firestore Callable场景下多用户查询复用逻辑
需求背景
现有一个页面需要向服务端发起三类用户查询请求:
- 搜索框输入用户名时,按用户名查询匹配用户
- 页面挂载和下拉刷新时,查询 premium 用户列表
- 页面挂载和下拉刷新时,查询年轻用户列表
我希望将这部分逻辑抽为可复用的Hook,当前代码如下:
function MyScreen() { // Fetch by username const [isSearching, setIsSearching] = useState([]); const [searchedUsers, setSearchedUsers] = useState([]); // Premium const [isLoading, setIsLoading] = useState(true); <--- NOTE THIS const [premiumUsers, setPremiumUsers] = useState([]); // Young users const [youngUsers, setYoungUsers] = useState([]); /* FIRST METHOD */ const searchUsersByUsername = async (limit = 20) => { setIsSearching(true); try { const result = await api.users.searchUsersByUsername(username, limit); setSearchedUsers(result); } catch(err) { console.log(err); } setIsSearching(false); } /* SECOND METHOD */ const getPremiumUsers = async (limit = 10) => { // Note: No loading here try { const result = await api.users.getPremiumUsers(limit); setPremiumUsers(result); } catch(err) { console.log(err); } } /* THIRD METHOD */ const getYoungUsers = async (limit = 10) => { // Note: No loading here try { const result = await api.users.getYoungUsers(limit); setYoungUsersUsers(result); } catch(err) { console.log(err); } } // Effects and rendering... useEffect(() => { (async () => { const promises = [getPremiumUsers(), getYoungUsers()]; await Promise.all(promises).catch((err) => { console.log(err)) }); setIsLoading(false); // <---- NOTE THIS })(); }, []); }
遇到的问题
- 由于使用的是Firestore Callable Functions,无法使用常规的useQuery Hook,只能通过调用自定义api方法(如
api.users.searchUser等)发起请求。 - 当前代码中有两类加载状态:
isSearching对应用户名搜索的加载状态,isLoading对应premium用户和年轻用户并行查询的整体加载状态。
期望封装的Hook满足以下需求:
- 用户名搜索功能可在其他页面复用
- 支持单独查询premium用户列表,无需并行拉取年轻用户
- 支持单独查询年轻用户列表,无需并行拉取premium用户
- 支持获取三类查询各自的加载状态,也支持获取premium和年轻用户并行查询的整体加载状态,适配不同页面的需求
实现方案
设计思路
按功能拆分独立Hook保证复用性,同时提供组合查询能力适配多场景需求:
- 单独抽离用户名搜索Hook,可直接在任意需要搜索功能的页面引入
- 抽离用户列表查询Hook,支持单独查某类用户,也支持并行查两类用户,同时暴露各自的加载状态和整体加载状态
代码实现
1. 独立搜索Hook:useUserSearch.js
import { useState } from 'react'; import api from '你的api引入路径'; export default function useUserSearch() { // 修正原代码笔误:isSearching是布尔值,初始值为false const [isSearching, setIsSearching] = useState(false); const [searchedUsers, setSearchedUsers] = useState([]); /** * 按用户名搜索用户 * @param {string} username 搜索关键词 * @param {number} limit 返回结果条数 */ const searchUsersByUsername = async (username, limit = 20) => { setIsSearching(true); try { const result = await api.users.searchUsersByUsername(username, limit); setSearchedUsers(result); } catch (err) { console.error('搜索用户失败:', err); } finally { setIsSearching(false); } }; // 清空搜索结果,方便页面重置状态 const clearSearchResult = () => { setSearchedUsers([]); }; return { isSearching, searchedUsers, searchUsersByUsername, clearSearchResult }; }
2. 用户列表查询Hook:useUserList.js
import { useState } from 'react'; import api from '你的api引入路径'; export default function useUserList() { // premium用户相关状态 const [isLoadingPremium, setIsLoadingPremium] = useState(false); const [premiumUsers, setPremiumUsers] = useState([]); // 年轻用户相关状态 const [isLoadingYoung, setIsLoadingYoung] = useState(false); const [youngUsers, setYoungUsers] = useState([]); // 两类用户并行查询的整体加载状态 const [isLoadingAllList, setIsLoadingAllList] = useState(false); /** * 单独查询premium用户列表 * @param {number} limit 返回结果条数 */ const fetchPremiumUsers = async (limit = 10) => { setIsLoadingPremium(true); try { const result = await api.users.getPremiumUsers(limit); setPremiumUsers(result); } catch (err) { console.error('获取premium用户失败:', err); } finally { setIsLoadingPremium(false); } }; /** * 单独查询年轻用户列表 * @param {number} limit 返回结果条数 */ const fetchYoungUsers = async (limit = 10) => { setIsLoadingYoung(true); try { // 修正原代码笔误:setYoungUsersUsers改为setYoungUsers const result = await api.users.getYoungUsers(limit); setYoungUsers(result); } catch (err) { console.error('获取年轻用户失败:', err); } finally { setIsLoadingYoung(false); } }; /** * 并行查询两类用户列表 * @param {number} premiumLimit premium用户返回条数 * @param {number} youngLimit 年轻用户返回条数 */ const fetchAllUserList = async (premiumLimit = 10, youngLimit = 10) => { setIsLoadingAllList(true); try { await Promise.all([ fetchPremiumUsers(premiumLimit), fetchYoungUsers(youngLimit) ]); } catch (err) { console.error('批量获取用户列表失败:', err); } finally { setIsLoadingAllList(false); } }; return { // premium用户相关 isLoadingPremium, premiumUsers, fetchPremiumUsers, // 年轻用户相关 isLoadingYoung, youngUsers, fetchYoungUsers, // 并行查询相关 isLoadingAllList, fetchAllUserList }; }
页面使用示例
全功能场景(需要搜索+两类列表)
import { useEffect } from 'react'; import useUserSearch from './useUserSearch'; import useUserList from './useUserList'; function MyScreen() { // 引入搜索逻辑 const { isSearching, searchedUsers, searchUsersByUsername, clearSearchResult } = useUserSearch(); // 引入列表逻辑 const { isLoadingAllList, premiumUsers, youngUsers, fetchAllUserList } = useUserList(); // 页面挂载时拉取两类用户列表 useEffect(() => { fetchAllUserList(); }, []); // 搜索框输入回调 const handleSearchInput = (e) => { const username = e.target.value.trim(); if (!username) { clearSearchResult(); return; } // 可自行添加防抖逻辑 searchUsersByUsername(username); }; // 下拉刷新回调 const handleRefresh = () => { fetchAllUserList(); }; // 渲染逻辑省略 return <div>页面内容</div> }
单列表场景(仅需要年轻用户)
import { useEffect } from 'react'; import useUserList from './useUserList'; function YoungUserPage() { const { isLoadingYoung, youngUsers, fetchYoungUsers } = useUserList(); useEffect(() => { fetchYoungUsers(20); }, []); // 渲染逻辑省略 return <div>年轻用户列表</div> }
内容的提问来源于stack exchange,提问作者Raul
相关产品推荐
相关产品推荐

