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

如何传入更新后的参数重新调用useCollection自定义Hook

enter image description here

问题描述

Home组件首次挂载时会传入初始参数调用useCollection Hook实现列表数据拉取,现在需要传入不同参数重新触发Hook执行,实现列表过滤功能。
实际操作中两个方案都失败:

  • 在按钮onClick事件回调里直接调用Hook,直接抛出错误
  • 用useState更新传入Hook的参数,无法触发Hook重新执行查询

现有useCollection Hook源码

import React, { useEffect, useRef, useState } from "react";
import { projectFirestore } from "../firebase/config";

export const useCollections = (collection, _query, _orderBy) => {
const [documents, setDocuments] = useState(null);
const [error, setError] = useState(null);

const query = useRef(_query).current;
const orderBy = useRef(_orderBy).current;

useEffect(() => {

   let ref = projectFirestore.collection(collection);

   if (query) {
     ref = ref.where(...query);
   }

   if (orderBy) {
     ref = ref.orderBy(...orderBy);
   }

   const unsubscribe = ref.onSnapshot(
     (snapshot) => {
       let results = [];
       snapshot.docs.forEach((doc) => {
         results.push({ ...doc.data(), id: doc.id });
       });

       setDocuments(results);
       setError(null);
     },
     (error) => {
       console.log(error);
       setError("could not fetch data");
     }
   );

   return () => unsubscribe();
 }, [collection, query, orderBy]);

 return { documents, error };
};
核心原因

两个问题分别对应React Hook的基础规则和现有Hook的实现bug:

  • 违反Hook调用规则:Hook只能在组件/自定义Hook的顶层作用域调用,事件回调、条件判断、循环里都不能直接调用Hook,React无法在这些上下文里正确追踪Hook的状态链路,所以会直接抛错。
  • Hook内部ref写法错误:useRef(_query).current只会在组件首次渲染时把传入的_query存为ref初始值,后续父组件传入新的查询参数时,ref保存的值不会同步更新,导致useEffect依赖的query/orderBy永远是初始值,参数变化不会触发effect重跑。
实现步骤

1. 修复自定义Hook的参数响应逻辑

去掉错误的ref固定取值写法,对引用类型的查询参数做序列化处理避免依赖项误触发,修改后的Hook代码:

import React, { useEffect, useState } from "react";
import { projectFirestore } from "../firebase/config";

export const useCollections = (collection, _query, _orderBy) => {
  const [documents, setDocuments] = useState(null);
  const [error, setError] = useState(null);

  // 序列化数组/对象类型参数,既保证值变化能被依赖检测到,又避免引用类型每次渲染生成新引用导致的重复执行
  const queryStr = JSON.stringify(_query);
  const orderByStr = JSON.stringify(_orderBy);

  useEffect(() => {
    const query = JSON.parse(queryStr);
    const orderBy = JSON.parse(orderByStr);

    let ref = projectFirestore.collection(collection);

    if (query) {
      ref = ref.where(...query);
    }

    if (orderBy) {
      ref = ref.orderBy(...orderBy);
    }

    const unsubscribe = ref.onSnapshot(
      (snapshot) => {
        let results = [];
        snapshot.docs.forEach((doc) => {
          results.push({ ...doc.data(), id: doc.id });
        });
        setDocuments(results);
        setError(null);
      },
      (error) => {
        console.log(error);
        setError("数据拉取失败");
      }
    );

    return () => unsubscribe();
  }, [collection, queryStr, orderByStr]);

  return { documents, error };
};

2. 业务组件中用state维护查询参数

永远在组件顶层调用Hook,通过更新state的方式触发Hook参数变化,示例代码:

import { useState } from 'react'
import { useCollections } from '你的Hook路径/useCollections'

const Home = () => {
  // 用state维护查询条件,初始值为默认过滤规则,不需要默认过滤就传null
  const [listQuery, setListQuery] = useState(null)
  // 顶层调用Hook,不要放到事件、条件判断里
  const { documents: list, error } = useCollections('posts', listQuery, ['createTime', 'desc'])

  // 点击按钮只更新查询参数state,Hook会自动感知参数变化重跑查询
  const handleFilterTech = () => {
    setListQuery(['category', '==', 'tech'])
  }

  const handleReset = () => {
    setListQuery(null)
  }

  return (
    <div className="home">
      <div className="filter-bar">
        <button onClick={handleFilterTech}>只看技术类内容</button>
        <button onClick={handleReset}>重置筛选</button>
      </div>
      {error && <p className="error">{error}</p>}
      {list && (
        <div className="list">
          {list.map(item => (
            <div key={item.id} className="list-item">{item.title}</div>
          ))}
        </div>
      )}
    </div>
  )
}

export default Home

如果需要多条件查询,直接把查询条件组装为符合Firestore要求的格式传入即可,多条件时可以把where条件做循环遍历处理,适配多个查询规则。

内容的提问来源于stack exchange,提问作者Shivang Mathur

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 20:54:19