You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Hooks封装Firestore Callable场景下多用户查询复用逻辑

需求背景

现有一个页面需要向服务端发起三类用户查询请求:

  1. 搜索框输入用户名时,按用户名查询匹配用户
  2. 页面挂载和下拉刷新时,查询 premium 用户列表
  3. 页面挂载和下拉刷新时,查询年轻用户列表

我希望将这部分逻辑抽为可复用的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满足以下需求:

  1. 用户名搜索功能可在其他页面复用
  2. 支持单独查询premium用户列表,无需并行拉取年轻用户
  3. 支持单独查询年轻用户列表,无需并行拉取premium用户
  4. 支持获取三类查询各自的加载状态,也支持获取premium和年轻用户并行查询的整体加载状态,适配不同页面的需求

实现方案

设计思路

按功能拆分独立Hook保证复用性,同时提供组合查询能力适配多场景需求:

  1. 单独抽离用户名搜索Hook,可直接在任意需要搜索功能的页面引入
  2. 抽离用户列表查询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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 23:18:02