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

Redux Toolkit中filterSlice未配置进Store致ViewProducts页面报错

Redux Toolkit 商品筛选时出现 TypeError:无法读取 undefined 的 slice/length 属性

我正在开发基于Firebase的电商网站,后台管理员路由已完成商品添加功能,现在要实现商品查看、筛选、编辑等功能。作为Redux Toolkit新手,在实现商品搜索筛选时遇到了以下问题:

  • 已创建filterSlice.jsx实现搜索、分类、价格排序等筛选逻辑
  • 在ViewProducts.jsx中通过useSelector调用selectFilteredProducts选择器获取筛选后的商品列表
  • 控制台持续抛出TypeError,提示无法读取undefined的slice或length属性

问题原因

排查后确认,核心问题是store.jsx的combineReducers中未注册filterReducer。Redux store中不存在filter状态分支,导致selectFilteredProducts选择器返回undefined,后续访问filteredProducts的length或调用slice()时自然触发报错。

解决方案

在store.jsx的rootReducer中添加filter: filterReducer,将filter切片正式注册到Redux store中:

修正后的store.jsx

import { configureStore, combineReducers } from "@reduxjs/toolkit"
import authReducer from "../redux/slice/authSlice"
import productReducer from "../redux/slice/productSlice"
import filterReducer from "../redux/slice/filterSlice"

const rootReducer = combineReducers({
  auth: authReducer,
  product: productReducer,
  filter: filterReducer, // 新增该行,注册filter状态分支
})

const store = configureStore({
  reducer: rootReducer,
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware({
      serializableCheck: false,
    }),
});

export default store

额外代码问题提示

你的ViewProducts.jsx中还有几个小问题需要修正,避免后续报错:

  1. isLoading变量未定义,需补充初始化或从Redux状态中获取
  2. 列表循环时参数名与解构变量不匹配:循环参数写的是products,但解构用了product,会导致product为undefined
  3. imageURl拼写错误,应为imageURL,否则图片无法正常加载
  4. 建议给filteredProducts添加可选链判断,避免状态未初始化时报错

修正后的ViewProducts.jsx关键部分

import React, { useEffect, useState } from "react";
// ...其他导入代码

const ViewProducts = () => {
  const [search, setSearch] = useState("")
  const [isLoading, setIsLoading] = useState(false); // 初始化isLoading
  const products = useSelector(selectProducts);
  const filteredProducts = useSelector(selectFilteredProducts);
  // 分页状态
  const [currentPage, setCurrentPage] = useState(1);
  const [productsPerPage, setProductsPerPage] = useState(10);
  
  // 获取当前页商品,添加可选链避免undefined报错
  const indexOfLastProduct = currentPage * productsPerPage;
  const indexOfFirstProduct = indexOfLastProduct - productsPerPage;
  const currentProducts = filteredProducts?.slice(
    indexOfFirstProduct,
    indexOfLastProduct
  ) || [];

  const dispatch = useDispatch()

  // 监听搜索词变化,触发筛选
  useEffect(() => {
    dispatch(FILTER_BY_SEARCH({ products, search }));
  }, [dispatch, products, search]);

  return (
    <>
      {isLoading && <Loader />}
      <div>
        <h2>All Products</h2>
        <p>
          <b>{filteredProducts?.length || 0}</b> products found
        </p>

        {(filteredProducts?.length || 0) === 0 ? (
          <p>NO Products found</p>
        ) : (
          <div className="table container-fluid ">
            <table className="table ">
              <thead className="product-list justify-content-between align-items-center text-center">
                <tr>
                  <th className=" col">S/N</th>
                  <th className=" col-2">Image</th>
                  <th className=" col-4">Name</th>
                  <th className=" col-2">Category</th>
                  <th className=" col-2">Price</th>
                  <th className=" col-2">Action</th>
                </tr>
              </thead>
              <tbody className="table-body justify-content-between align-items-center text-center">
                {/* 修正循环参数名,匹配解构变量 */}
                {currentProducts.map((product, index) => {
                  const { id, name, price, imageURL, category } = product; // 修正图片字段拼写
                  return (
                    <tr key={id}>
                      <td>{index + 1}</td>
                      <td>
                        <img
                          src={imageURL}
                          alt={name}
                          style={{ width: "100px" }}
                        />
                      </td>
                      <td>{name}</td>
                      <td>{category}</td>
                      <td>{`$${price}`}</td>
                      <td>
                        <FaEdit size={20} color="green" />
                        &nbsp;
                        <FaTrashAlt size={18} color="red" />
                      </td>
                    </tr>
                  );
                })}
              </tbody>
            </table>
          </div>
        )}
      </div>
    </>
  );
};

export default ViewProducts;

完整filterSlice.jsx参考

import { createSlice } from "@reduxjs/toolkit";

const initialState = {
  filteredProducts: []
};

const filterSlice = createSlice({
  name: "filter",
  initialState,
  reducers: {
    FILTER_BY_SEARCH(state, action) {
      const { products, search } = action.payload;
      const tempProducts = products.filter(
        (product) =>
          product.name.toLowerCase().includes(search.toLowerCase()) ||
          product.category.toLowerCase().includes(search.toLowerCase())
      );

      state.filteredProducts = tempProducts;
    },
    SORT_PRODUCTS(state, action) {
      const { products, sort } = action.payload;
      let tempProducts = [];
      if (sort === "latest") {
        tempProducts = products;
      }

      if (sort === "lowest-price") {
        tempProducts = products.slice().sort((a, b) => {
          return a.price - b.price;
        });
      }

      if (sort === "highest-price") {
        tempProducts = products.slice().sort((a, b) => {
          return b.price - a.price;
        });
      }

      if (sort === "a-z") {
        tempProducts = products.slice().sort((a, b) => {
          return a.name.localeCompare(b.name);
        });
      }

      if (sort === "z-a") {
        tempProducts = products.slice().sort((a, b) => {
          return b.name.localeCompare(a.name);
        });
      }

      state.filteredProducts = tempProducts;
    },
    FILTER_BY_CATEGORY(state, action) {
      const { products, category } = action.payload;
      let tempProducts = [];
      if (category === "All") {
        tempProducts = products;
      } else {
        tempProducts = products.filter(
          (product) => product.category === category
        );
      }
      state.filteredProducts = tempProducts;
    },

    FILTER_BY_BRAND(state, action) {
      const { products, brand } = action.payload;
      let tempProducts = [];
      if (brand === "All") {
        tempProducts = products;
      } else {
        tempProducts = products.filter((product) => product.brand === brand);
      }
      state.filteredProducts = tempProducts;
    },
    FILTER_BY_PRICE(state, action) {
      const { products, price } = action.payload;
      let tempProducts = [];
      tempProducts = products.filter((product) => product.price <= price);

      state.filteredProducts = tempProducts;
    }
  }
});

export const {
  FILTER_BY_SEARCH,
  SORT_PRODUCTS,
  FILTER_BY_CATEGORY,
  FILTER_BY_BRAND,
  FILTER_BY_PRICE
} = filterSlice.actions;

export const selectFilteredProducts = (state) => state.filter && state.filter.filteredProducts;

export default filterSlice.reducer;

内容的提问来源于stack exchange,提问作者Cable-Nerd

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 14:20:34