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

React Redux Toolkit中useGetFilterProductsQuery等钩子未定义问题求助

问题排查与修复:Redux Toolkit Query 钩子不存在报错

报错信息

app_api_apiSlice__WEBPACK_IMPORTED_MODULE_1_.useGetFilterProductsByCategoriesQuery is not a function
ProductList@http://localhost:3000/static/js/bundle.js:781:108
...(省略后续栈信息)

用户代码

ProductList.js

import React from 'react';
import { Container, Row, Col, Card, Button } from 'react-bootstrap';

import { useGetProductsQuery, useGetFilterProductsQuery, useGetFilterProductsByCategoriesQuery } from '../../app/api/apiSlice';
import { useSelector } from 'react-redux';

function ProductList() {
  const selectedCategories = useSelector(state => state.filters.selectedCategories);
  const minPrice = useSelector(state => state.filters.minPrice);
  const maxPrice = useSelector(state => state.filters.maxPrice);

  let productQuery;

  if (minPrice && maxPrice) {
    alert(minPrice + " " + maxPrice);
    productQuery = useGetFilterProductsQuery(minPrice, maxPrice );
  } else if (selectedCategories.length > 0) {
    alert(selectedCategories);
    productQuery = useGetFilterProductsByCategoriesQuery(selectedCategories);
  } else {
    console.log(`It is working fine!`);
    productQuery = useGetProductsQuery();
  }

  const { data: products, isLoading, isError, isSuccess } = productQuery;

  if (isLoading) {
    return <p>Loading...</p>;
  }

  if (isError) {
    console.log('Error' + isError);
    return <p>Error fetching products</p>;
  }
  
  return (
    <Container>
      <h1 className="my-4">Product List</h1>
      <Row>
        {products.products.map((product) => (
          <Col key={product.id} xs={12} md={6} lg={4}>
            <Card className="mb-4">
              <Card.Img variant="top" src={product.image} alt={product.name} />
              <Card.Body>
                <Card.Title>{product.name}</Card.Title>
                <Card.Text>{product.description}</Card.Text>
                <Card.Text>Price: ${product.price}</Card.Text>
                <Button
                  variant="primary"
                  onClick={() => handleAddToCart(product.id)}
                >
                  Add to Cart
                </Button>
              </Card.Body>
            </Card>
          </Col>
        ))}
      </Row>
    </Container>
  );
}

export default ProductList;

apiSlice.js

// Need to use the React-specific entry point to import createApi
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'

// Define a service using a base URL and expected endpoints
export const productApi = createApi({
  reducerPath: 'productApi',
  baseQuery: fetchBaseQuery({ baseUrl: 'http://localhost:5000/' }),
   endpoints: (builder) => ({
    getProducts: builder.query({
      query: () => 'product', // Change this to match your API endpoint for fetching products
    }),
    getCategory: builder.query({
      query: () => 'categories', // Change this to match your API endpoint for fetching products
    }),
   
    getfilterProducts: builder.query({
        query: ({minPrice,maxPrice}) => `/products/filterbypricerange?minPrice=${minPrice}&maxPrice=${maxPrice}`,
    }),
     
     getfilterProductsByCategories: builder.query({
      query: (categories) => `/products/categories=${categories}`,
     }),  
  }),
})

// Export hooks for usage in functional components, which are
// auto-generated based on the defined endpoints
export const { useGetProductsQuery,useGetCategoryQuery, useGetFilterProductsQuery,useGetFilterProductsByCategoriesQuery } = productApi;

问题分析与修复步骤

1. 钩子命名不匹配(核心报错原因)

RTK Query 自动生成的钩子名称遵循 use[EndpointNamePascalCase]Query 规则,其中 EndpointNamePascalCase 是 endpoint 键名的大驼峰形式。

  • apiSlice 中 endpoint 键名是 getfilterProductsByCategories(filter 首字母小写),自动生成的钩子应为 useGetfilterProductsByCategoriesQuery,但导出的是 useGetFilterProductsByCategoriesQuery(Filter 首字母大写),导致找不到该函数。
  • 同理,getfilterProducts 对应的正确钩子是 useGetfilterProductsQuery,导出的 useGetFilterProductsQuery 也不匹配。

修复方案:统一 endpoint 键名的驼峰命名,改为规范的大驼峰开头:

// apiSlice.js 中修改 endpoints 部分
endpoints: (builder) => ({
  // ...其他保留
  getFilterProducts: builder.query({
    query: ({minPrice,maxPrice}) => `/products/filterbypricerange?minPrice=${minPrice}&maxPrice=${maxPrice}`,
  }),
   
  getFilterProductsByCategories: builder.query({
    query: (categories) => `/products/categories=${categories}`,
  }),  
}),

此时 RTK 会自动生成 useGetFilterProductsQuery 和 useGetFilterProductsByCategoriesQuery 钩子,与导出名称完全匹配。

2. 违反 React Hooks 调用规则

在 if/else 条件语句中调用 RTK Query 钩子,违反了 Hooks 必须在组件顶层调用 的规则,会导致 React 状态异常。

修复方案:在组件顶层调用所有需要的钩子,通过 skip 参数控制是否发起请求,再根据条件选择使用对应的查询结果:

// ProductList.js 中修改查询部分
function ProductList() {
  const selectedCategories = useSelector(state => state.filters.selectedCategories);
  const minPrice = useSelector(state => state.filters.minPrice);
  const maxPrice = useSelector(state => state.filters.maxPrice);

  // 顶层调用所有钩子,通过 skip 控制请求触发
  const { data: allProducts, isLoading: isLoadingAll, isError: isErrorAll } = useGetProductsQuery(
    {}, 
    { skip: !!minPrice || !!maxPrice || selectedCategories.length > 0 }
  );
  const { data: priceFilteredProducts, isLoading: isLoadingPrice, isError: isErrorPrice } = useGetFilterProductsQuery(
    { minPrice, maxPrice }, 
    { skip: !minPrice || !maxPrice || selectedCategories.length > 0 }
  );
  const { data: categoryFilteredProducts, isLoading: isLoadingCategory, isError: isErrorCategory } = useGetFilterProductsByCategoriesQuery(
    selectedCategories, 
    { skip: selectedCategories.length === 0 || !!minPrice || !!maxPrice }
  );

  // 根据条件选择对应的结果
  let products, isLoading, isError;
  if (minPrice && maxPrice) {
    products = priceFilteredProducts;
    isLoading = isLoadingPrice;
    isError = isErrorPrice;
  } else if (selectedCategories.length > 0) {
    products = categoryFilteredProducts;
    isLoading = isLoadingCategory;
    isError = isErrorCategory;
  } else {
    products = allProducts;
    isLoading = isLoadingAll;
    isError = isErrorAll;
  }

  // ...后续逻辑不变
}

3. 查询参数传递错误

useGetFilterProductsQuery 的 endpoint 定义中,query 函数接收的是对象 {minPrice, maxPrice},但调用时传了两个独立参数,导致参数无法正确解析。

修复方案:传递对象参数(上述修复代码已包含此修改):

useGetFilterProductsQuery({ minPrice, maxPrice }, { skip: ... })

4. 未定义 handleAddToCart 函数

组件中使用了 handleAddToCart 但未定义,点击按钮会报错。

修复方案:补充函数定义(示例,根据实际需求调整):

function ProductList() {
  // ...其他代码

  const handleAddToCart = (productId) => {
    // 实现添加到购物车的逻辑,比如 dispatch Redux action
    console.log('Added product to cart:', productId);
  };

  // ...后续逻辑不变
}

最终修正后的核心代码片段

apiSlice.js(关键修改)

export const productApi = createApi({
  reducerPath: 'productApi',
  baseQuery: fetchBaseQuery({ baseUrl: 'http://localhost:5000/' }),
   endpoints: (builder) => ({
    getProducts: builder.query({
      query: () => 'product',
    }),
    getCategory: builder.query({
      query: () => 'categories',
    }),
    // 修正 endpoint 命名为规范驼峰
    getFilterProducts: builder.query({
        query: ({minPrice,maxPrice}) => `/products/filterbypricerange?minPrice=${minPrice}&maxPrice=${maxPrice}`,
    }),
    // 修正 endpoint 命名为规范驼峰
    getFilterProductsByCategories: builder.query({
      query: (categories) => `/products/categories=${categories}`,
    }),  
  }),
})

// 导出的钩子现在与自动生成的名称完全匹配
export const { useGetProductsQuery, useGetCategoryQuery, useGetFilterProductsQuery, useGetFilterProductsByCategoriesQuery } = productApi;

ProductList.js(关键修改)

function ProductList() {
  const selectedCategories = useSelector(state => state.filters.selectedCategories);
  const minPrice = useSelector(state => state.filters.minPrice);
  const maxPrice = useSelector(state => state.filters.maxPrice);

  // 顶层调用所有钩子,通过 skip 控制请求触发
  const { data: allProducts, isLoading: isLoadingAll, isError: isErrorAll } = useGetProductsQuery(
    {}, 
    { skip: !!minPrice || !!maxPrice || selectedCategories.length > 0 }
  );
  const { data: priceFilteredProducts, isLoading: isLoadingPrice, isError: isErrorPrice } = useGetFilterProductsQuery(
    { minPrice, maxPrice }, 
    { skip: !minPrice || !maxPrice || selectedCategories.length > 0 }
  );
  const { data: categoryFilteredProducts, isLoading: isLoadingCategory, isError: isErrorCategory } = useGetFilterProductsByCategoriesQuery(
    selectedCategories, 
    { skip: selectedCategories.length === 0 || !!minPrice || !!maxPrice }
  );

  // 选择当前有效的查询结果
  let products, isLoading, isError;
  if (minPrice && maxPrice) {
    products = priceFilteredProducts;
    isLoading = isLoadingPrice;
    isError = isErrorPrice;
  } else if (selectedCategories.length > 0) {
    products = categoryFilteredProducts;
    isLoading = isLoadingCategory;
    isError = isErrorCategory;
  } else {
    products = allProducts;
    isLoading = isLoadingAll;
    isError = isErrorAll;
  }

  // 补充 handleAddToCart 函数定义
  const handleAddToCart = (productId) => {
    // 替换为你的实际购物车逻辑
    console.log(`Added product ${productId} to cart`);
  };

  if (isLoading) {
    return <p>Loading...</p>;
  }

  if (isError) {
    console.error('Error fetching products:', isError);
    return <p>Error fetching products</p>;
  }

  // 确保 products 存在再渲染
  if (!products?.products) return <p>No products found</p>;
  
  return (
    <Container>
      <h1 className="my-4">Product List</h1>
      <Row>
        {products.products.map((product) => (
          <Col key={product.id} xs={12} md={6} lg={4}>
            <Card className="mb-4">
              <Card.Img variant="top" src={product.image} alt={product.name} />
              <Card.Body>
                <Card.Title>{product.name}</Card.Title>
                <Card.Text>{product.description}</Card.Text>
                <Card.Text>Price: ${product.price}</Card.Text>
                <Button
                  variant="primary"
                  onClick={() => handleAddToCart(product.id)}
                >
                  Add to Cart
                </Button>
              </Card.Body>
            </Card>
          </Col>
        ))}
      </Row>
    </Container>
  );
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 14:37:32