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

如何在ReactJS中通过React Bootstrap下拉组件实现商品筛选与排序

Hey there! Let’s knock out this filter and sort feature quickly—you’ve got this in a single day. I’ll walk you through a complete, easy-to-follow solution that fits your existing Redux-Saga setup and component structure:

Step 1: Update the Dropdown Component to Handle Selection

First, we need to make your Dropdown actually communicate when a user selects an option. We’ll add a callback prop and update the toggle to show the selected value for better UX.

// Dropdown/index.js
import React from "react";
import { Dropdown as BootstrapDropdown } from "react-bootstrap";
import PropTypes from "prop-types";
import "../Dropdown/index.css";

const Dropdown = ({ items, onSelect, selectedValue }) => {
  return (
    <BootstrapDropdown className="sort-dropdown">
      <BootstrapDropdown.Toggle 
        className="sort-dropdown-toggle" 
        variant="success" 
        id="dropdown"
      >
        {/* Show selected value instead of static text */}
        <span className="toggle-text">{selectedValue || "Selection"}</span>
      </BootstrapDropdown.Toggle>
      <BootstrapDropdown.Menu className="sort-dropdown-menu">
        {items.map((name, index) => (
          <BootstrapDropdown.Item 
            className={`sort-dropdown-item ${name === selectedValue ? "active" : ""}`}
            key={index} 
            onClick={() => onSelect(name)} // Trigger selection callback
          >
            {name}
          </BootstrapDropdown.Item>
        ))}
      </BootstrapDropdown.Menu>
    </BootstrapDropdown>
  );
};

Dropdown.propTypes = {
  items: PropTypes.array.isRequired,
  onSelect: PropTypes.func.isRequired,
  selectedValue: PropTypes.string,
};

Dropdown.defaultProps = {
  items: [],
  selectedValue: "",
};

export default Dropdown;

Step 2: Add State & Filter Logic to GroupBar

Next, we’ll make GroupBar track the selected filter values and pass them up to the Product page to trigger Redux actions. We’ll also add "All" and "Default" options to let users reset filters.

// GroupBar/index.js
import React, { useState } from "react";
import { Row } from "react-bootstrap";
import Group from "../../../components/Group/index";
import Dropdown from "../../../components/Dropdown/index";
import "../GroupBar/index.css";

const GroupBar = ({ onFilterChange }) => {
  // Track selected filter values locally
  const [selectedCategory, setSelectedCategory] = useState("All");
  const [selectedSort, setSelectedSort] = useState("Default");

  const handleCategorySelect = (value) => {
    setSelectedCategory(value);
    // Pass filter data up (empty string for "All" to reset)
    onFilterChange({ 
      category: value === "All" ? "" : value, 
      sort: selectedSort 
    });
  };

  const handleSortSelect = (value) => {
    setSelectedSort(value);
    onFilterChange({ 
      category: selectedCategory === "All" ? "" : selectedCategory, 
      sort: value 
    });
  };

  return (
    <Row className="group-bar">
      <Group 
        title="Product group" 
        element={<Dropdown 
          items={["All", "Milk Tea", "Juice"]}
          onSelect={handleCategorySelect} 
          selectedValue={selectedCategory}
        />} 
      />
      <Group 
        title="Sort by price" 
        element={<Dropdown 
          items={["Default", "Low to high", "High to low"]}
          onSelect={handleSortSelect} 
          selectedValue={selectedSort}
        />} 
      />
    </Row>
  );
};

GroupBar.propTypes = {
  onFilterChange: PropTypes.func.isRequired,
};

export default GroupBar;

Step 3: Update Product Page to Trigger Redux Actions

Now, connect the GroupBar’s filter changes to your Redux store. We’ll use useDispatch to send filter actions.

// Product/index.js
import React from "react";
import { Container } from "react-bootstrap";
import GroupBar from "./GroupBar";
import ProductContainer from "../../containers/ProductContainer";
import Carousel from "../../components/Carousels";
import "../Product/index.css";
import { useDispatch } from "react-redux";
import { setFilters, fetchFilteredProducts } from "../../actions/productActions";

const Product = () => {
  const dispatch = useDispatch();

  const handleFilterChange = (filters) => {
    // Update filters in Redux and trigger data fetch/sorting
    dispatch(setFilters(filters));
    dispatch(fetchFilteredProducts());
  };

  return (
    <Container fluid className="p-0">
      <Carousel />
      <Container>
        <GroupBar onFilterChange={handleFilterChange} />
        <ProductContainer />
      </Container>
    </Container>
  );
};

export default Product;

Step 4: Extend Redux Actions, Reducer & Saga

We need to update your Redux setup to handle filters. First, add new action types:

// actions/productActions.js
export const FETCH_PRODUCT_REQUEST = "FETCH_PRODUCT_REQUEST";
export const FETCH_PRODUCT_SUCCESS = "FETCH_PRODUCT_SUCCESS";
export const FETCH_PRODUCT_FAILURE = "FETCH_PRODUCT_FAILURE";
export const SET_FILTERS = "SET_FILTERS";
export const FETCH_FILTERED_PRODUCTS = "FETCH_FILTERED_PRODUCTS";

// Existing actions
export const fetchProductRequest = () => ({ type: FETCH_PRODUCT_REQUEST });
export const fetchProductSuccess = (products) => ({ type: FETCH_PRODUCT_SUCCESS, payload: products });
export const fetchProductFailure = (error) => ({ type: FETCH_PRODUCT_FAILURE, payload: error });

// New filter actions
export const setFilters = (filters) => ({ type: SET_FILTERS, payload: filters });
export const fetchFilteredProducts = () => ({ type: FETCH_FILTERED_PRODUCTS });

Update the reducer to track filters:

// reducers/productReducer.js
import {
  FETCH_PRODUCT_REQUEST,
  FETCH_PRODUCT_SUCCESS,
  FETCH_PRODUCT_FAILURE,
  SET_FILTERS,
} from "../actions/productActions";

const initialState = {
  products: [],
  loading: false,
  error: null,
  filters: { category: "", sort: "" }, // Track filter state
};

const productReducer = (state = initialState, action) => {
  switch (action.type) {
    case FETCH_PRODUCT_REQUEST:
      return { ...state, loading: true, error: null };
    case FETCH_PRODUCT_SUCCESS:
      return { ...state, loading: false, products: action.payload };
    case FETCH_PRODUCT_FAILURE:
      return { ...state, loading: false, error: action.payload };
    case SET_FILTERS:
      return { ...state, filters: action.payload };
    default:
      return state;
  }
};

export default productReducer;

Choose one of these two options based on your needs:

Option A: Frontend Filtering (Fastest to Implement)

If you don’t need to hit the backend, handle filtering directly in your container component. Skip updating the saga and modify ProductContainer:

// containers/ProductContainer.js
import React, { useEffect } from "react";
import { connect } from "react-redux";
import ProductList from "../components/Product/ProductList";
import { fetchProductRequest } from "../actions/productActions";

const ProductContainer = ({ products, loading, filters, fetchProductRequest }) => {
  useEffect(() => {
    fetchProductRequest();
  }, [fetchProductRequest]);

  // Apply frontend filtering and sorting
  let filteredProducts = [...products];
  
  // Filter by category
  if (filters.category) {
    filteredProducts = filteredProducts.filter(product => 
      product.category === filters.category
    );
  }

  // Sort by price
  if (filters.sort === "Low to high") {
    filteredProducts.sort((a, b) => a.price - b.price);
  } else if (filters.sort === "High to low") {
    filteredProducts.sort((a, b) => b.price - a.price);
  }

  return (
    <ProductList 
      products={filteredProducts} 
      loading={loading} 
      fetchProductRequest={fetchProductRequest} 
    />
  );
};

const mapStateToProps = (state) => ({
  products: state.products.products,
  loading: state.products.loading,
  filters: state.products.filters,
});

const mapDispatchToProps = {
  fetchProductRequest,
};

export default connect(mapStateToProps, mapDispatchToProps)(ProductContainer);

Option B: Backend Filtering (For Large Datasets)

If you need to fetch filtered data from your API, update the saga:

// sagas/productSagas.js
import { call, put, takeLatest, select } from "redux-saga/effects";
import {
  FETCH_PRODUCT_REQUEST,
  FETCH_FILTERED_PRODUCTS,
  fetchProductSuccess,
  fetchProductFailure,
} from "../actions/productActions";
import { getProducts, getFilteredProducts } from "../api/productApi";

// Get current filters from Redux state
const getCurrentFilters = (state) => state.products.filters;

function* fetchProductsSaga() {
  try {
    const products = yield call(getProducts);
    yield put(fetchProductSuccess(products));
  } catch (error) {
    yield put(fetchProductFailure(error.message));
  }
}

function* fetchFilteredProductsSaga() {
  try {
    const filters = yield select(getCurrentFilters);
    // Pass filters to your API
    const products = yield call(getFilteredProducts, filters);
    yield put(fetchProductSuccess(products));
  } catch (error) {
    yield put(fetchProductFailure(error.message));
  }
}

export default function* productSaga() {
  yield takeLatest(FETCH_PRODUCT_REQUEST, fetchProductsSaga);
  yield takeLatest(FETCH_FILTERED_PRODUCTS, fetchFilteredProductsSaga);
}

And your API function might look like this:

// api/productApi.js
export const getProducts = async () => {
  const res = await fetch("/api/products");
  return res.json();
};

export const getFilteredProducts = async (filters) => {
  const params = new URLSearchParams();
  if (filters.category) params.append("category", filters.category);
  if (filters.sort) {
    // Convert frontend sort text to backend-friendly param
    const sortParam = filters.sort === "Low to high" ? "price_asc" : "price_desc";
    params.append("sort", sortParam);
  }
  const res = await fetch(`/api/products?${params}`);
  return res.json();
};

Step 5: Test & Verify

  1. Start your app and check that selecting dropdown options updates the toggle text.
  2. Confirm that product list filters/sorts correctly when you select options.
  3. If using backend filtering, ensure your API receives the correct query parameters.

That’s it! This solution is tailored to your existing code, uses Redux-Saga as requested, and can be implemented quickly.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.09 06:28:14