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

基于Redux的React多筛选分页查询问题:如何维持不同搜索类型的分页功能

嘿,这个问题我之前也碰到过!核心就是要明确跟踪当前的搜索类型,让分页逻辑始终和当前类型绑定,同时在类型切换时重置分页状态。我给你一步步拆解解决方案:


第一步:统一管理搜索状态与分页信息

首先在Redux里新增状态字段,明确区分当前的搜索模式,同时维护分页所需的参数:

// carsSlice.js(用@reduxjs/toolkit实现)
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';

// 定义三种API调用的异步Thunk
export const fetchDefaultCars = createAsyncThunk(
  'cars/fetchDefault',
  async ({ limit, offset }) => {
    const res = await fetch(`https://api/cars?limit=${limit}&offset=${offset}`);
    return res.json();
  }
);

export const fetchCarsByKeyword = createAsyncThunk(
  'cars/fetchByKeyword',
  async ({ keyword, limit, offset }) => {
    const res = await fetch(`https://api/cars?keyword=${keyword}&limit=${limit}&offset=${offset}`);
    return res.json();
  }
);

export const fetchCarsByTags = createAsyncThunk(
  'cars/fetchByTags',
  async ({ tags, limit, offset }) => {
    const res = await fetch(`https://api/cars?tags=${tags.join(',')}&limit=${limit}&offset=${offset}`);
    return res.json();
  }
);

const carsSlice = createSlice({
  name: 'cars',
  initialState: {
    searchMode: 'default', // 可选值:'default' | 'keyword' | 'tags'
    keyword: '',
    tags: [],
    dataList: [],
    offset: 0,
    limit: 10,
    isLoading: false,
    hasMore: true // 是否还有更多数据可加载
  },
  reducers: {
    // 切换搜索模式时重置分页与数据
    switchSearchMode: (state, action) => {
      state.searchMode = action.payload.mode;
      state.keyword = action.payload.keyword || '';
      state.tags = action.payload.tags || [];
      state.offset = 0;
      state.dataList = [];
      state.hasMore = true;
    },
    // 分页时累加offset
    incrementOffset: (state) => {
      state.offset += state.limit;
    }
  },
  extraReducers: (builder) => {
    // 统一处理三种请求的加载状态与数据拼接
    const handleFulfilled = (state, action) => {
      state.isLoading = false;
      state.dataList = [...state.dataList, ...action.payload];
      state.hasMore = action.payload.length === state.limit;
    };
    const handlePending = (state) => { state.isLoading = true; };
    const handleRejected = (state) => { state.isLoading = false; };

    builder
      .addCase(fetchDefaultCars.pending, handlePending)
      .addCase(fetchDefaultCars.fulfilled, handleFulfilled)
      .addCase(fetchDefaultCars.rejected, handleRejected)
      .addCase(fetchCarsByKeyword.pending, handlePending)
      .addCase(fetchCarsByKeyword.fulfilled, handleFulfilled)
      .addCase(fetchCarsByKeyword.rejected, handleRejected)
      .addCase(fetchCarsByTags.pending, handlePending)
      .addCase(fetchCarsByTags.fulfilled, handleFulfilled)
      .addCase(fetchCarsByTags.rejected, handleRejected);
  }
});

export const { switchSearchMode, incrementOffset } = carsSlice.actions;
export default carsSlice.reducer;

第二步:组件中绑定搜索模式与分页逻辑

在组件里处理初始加载、搜索切换、滚动分页的逻辑,让所有操作都围绕当前的searchMode展开:

import { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { useLocation, useNavigate } from 'react-router-dom';
import { switchSearchMode, incrementOffset } from './carsSlice';

const CarsList = () => {
  const dispatch = useDispatch();
  const navigate = useNavigate();
  const location = useLocation();
  const { searchMode, keyword, tags, offset, limit, isLoading, hasMore, dataList } = useSelector(state => state.cars);

  // 从URL提取初始关键词(满足搜索框在其他路由的场景)
  const getInitialKeyword = () => {
    const params = new URLSearchParams(location.search);
    return params.get('keyword') || '';
  };

  // 页面初始加载:判断URL是否有关键词,初始化搜索模式
  useEffect(() => {
    const initialKeyword = getInitialKeyword();
    if (initialKeyword) {
      dispatch(switchSearchMode({ mode: 'keyword', keyword: initialKeyword }));
    } else {
      dispatch(switchSearchMode({ mode: 'default' }));
    }
  }, [dispatch, location.search]);

  // 根据当前搜索模式自动调用对应API(包括初始加载和分页)
  useEffect(() => {
    if (isLoading || !hasMore) return;
    
    switch (searchMode) {
      case 'keyword':
        dispatch(fetchCarsByKeyword({ keyword, limit, offset }));
        break;
      case 'tags':
        dispatch(fetchCarsByTags({ tags, limit, offset }));
        break;
      default:
        dispatch(fetchDefaultCars({ limit, offset }));
    }
  }, [searchMode, keyword, tags, offset, limit, isLoading, hasMore, dispatch]);

  // 搜索框输入处理(假设搜索框在其他页面,通过路由跳转同步状态)
  // 示例:在搜索组件中调用此方法
  // export const handleSearch = (inputKeyword) => {
  //   navigate(`/cars?keyword=${inputKeyword}`);
  //   dispatch(switchSearchMode({ mode: 'keyword', keyword: inputKeyword }));
  // };

  // 标签筛选按钮点击处理
  const handleTagFilter = (selectedTags) => {
    dispatch(switchSearchMode({ mode: 'tags', tags: selectedTags }));
    // 可选:同步标签到URL,刷新页面时恢复状态
    navigate(`/cars?tags=${selectedTags.join(',')}`);
  };

  // 滚动加载分页
  useEffect(() => {
    const handleScroll = () => {
      const isNearBottom = window.innerHeight + document.documentElement.scrollTop >= document.documentElement.offsetHeight - 100;
      if (isNearBottom && !isLoading && hasMore) {
        dispatch(incrementOffset());
      }
    };
    window.addEventListener('scroll', handleScroll);
    return () => window.removeEventListener('scroll', handleScroll);
  }, [isLoading, hasMore, dispatch]);

  return (
    <div>
      {/* 标签筛选按钮示例 */}
      <div className="tag-filters">
        <button onClick={() => handleTagFilter(['red'])}>红色车辆</button>
        <button onClick={() => handleTagFilter(['blue'])}>蓝色车辆</button>
        <button onClick={() => handleTagFilter(['red', 'blue'])}>红+蓝车辆</button>
      </div>

      {/* 数据列表展示 */}
      <ul className="cars-list">
        {dataList.map(car => (
          <li key={car.id}>{car.name}</li>
        ))}
      </ul>

      {isLoading && <div className="loading">加载中...</div>}
      {!hasMore && !isLoading && <div className="no-more">没有更多数据了</div>}
    </div>
  );
};

export default CarsList;

关键逻辑说明

  1. 搜索模式切换时重置状态:通过switchSearchMode action,每次切换搜索类型(默认/关键词/标签)都会重置offset为0、清空现有数据,避免不同类型的搜索结果混在一起。
  2. 分页与搜索模式绑定:分页时只更新offset,API调用逻辑会自动根据当前searchMode选择对应的接口,保证分页始终属于当前搜索类型。
  3. URL状态同步:搜索关键词和标签都同步到URL参数,刷新页面时能自动恢复之前的搜索状态,提升用户体验。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.27 18:03:10