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

React中如何设计统一管理filters与grids的公共体系减少冗余代码

通用Filter+Grid统一封装方案

完全可以实现逻辑统一封装,以下是可直接落地的实现方案,能解决多页面重复代码、Redux实现不一致的问题:


核心思路

把所有重复的列表加载、分页、筛选、Redux状态逻辑全部抽成通用公共层,每个页面仅需要传入差异化的配置参数、自定义UI渲染函数即可,不需要单独写业务逻辑。


具体实现步骤

1. 统一封装Redux公共逻辑

所有页面的列表状态共用同一份Redux逻辑,通过唯一命名空间区分不同页面的状态,避免冲突,不需要每个业务单独写Redux代码:

// src/common/grid/gridRedux.js
// 统一action类型
const GRID_SEARCH = "COMMON/GRID_SEARCH";
const GRID_LOAD_MORE = "COMMON/GRID_LOAD_MORE";
const GRID_RESET = "COMMON/GRID_RESET";

// 通用action生成函数
export const gridSearchAction = (namespace, payload) => ({
  type: GRID_SEARCH,
  namespace,
  payload
})
export const gridLoadMoreAction = (namespace, payload) => ({
  type: GRID_LOAD_MORE,
  namespace,
  payload
})
export const gridResetAction = (namespace) => ({
  type: GRID_RESET,
  namespace
})

// 通用reducer,按命名空间存储不同页面的列表状态
const initialState = {}
export default function gridReducer(state = initialState, action) {
  switch(action.type) {
    case GRID_SEARCH:
      return {
        ...state,
        [action.namespace]: {
          list: action.payload.list,
          searchParams: action.payload.searchParams,
          hasMore: action.payload.hasMore
        }
      }
    case GRID_LOAD_MORE:
      return {
        ...state,
        [action.namespace]: {
          ...state[action.namespace],
          list: [...state[action.namespace].list, ...action.payload.list],
          searchParams: action.payload.searchParams,
          hasMore: action.payload.hasMore
        }
      }
    case GRID_RESET:
      return {
        ...state,
        [action.namespace]: undefined
      }
    default:
      return state
  }
}

把上述gridReducer注册到全局Redux store中即可,所有页面的列表状态都在这里统一管理。


2. 封装通用Grid基础组件

把初始化加载、加载更多、参数处理等公共逻辑全部写在基础组件内,差异化逻辑通过props传入扩展:

// src/common/grid/BaseGrid.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { gridSearchAction, gridLoadMoreAction } from './gridRedux';

class BaseGrid extends Component {
  constructor(props) {
    super(props);
    this.loadMoreRef = React.createRef();
    this.state = {
      loaded: true
    };
  }

  componentDidMount() {
    this.handleSearch();
  }

  // 统一搜索逻辑
  handleSearch = (extraParams = {}) => {
    const { namespace, baseSearchParams, customParamHandler } = this.props;
    // 支持传入自定义参数处理函数,适配每个页面的少量调整
    const finalParams = customParamHandler ? 
      customParamHandler({...baseSearchParams, ...extraParams}) : 
      {...baseSearchParams, ...extraParams};
    this.props.gridSearchAction(namespace, finalParams);
  }

  // 统一加载更多逻辑
  loadMore = () => {
    const { namespace, gridData, customLoadMoreHandler } = this.props;
    const currentList = gridData?.list || [];
    // 无数据/无更多数据直接返回
    if(!currentList.length || !gridData?.hasMore) return;

    // 支持自定义加载更多逻辑,特殊页面可覆盖
    if(customLoadMoreHandler) {
      customLoadMoreHandler(gridData, this.props.gridLoadMoreAction);
      return;
    }

    const lastItem = currentList[currentList.length - 1];
    const searchPayload = gridData?.searchParams || {};
    let payload = { ...searchPayload };
    
    if (payload.sortBy) {
      payload.lastKey = payload.sortBy === "id" ? 
        lastItem[payload.sortBy] : 
        lastItem[payload.sortBy + 'Srt'];
    }

    this.props.gridLoadMoreAction(namespace, payload);
  }

  render() {
    const { gridData, renderFilter, renderItem } = this.props;
    return (
      <div className="common-grid">
        {/* 自定义筛选栏渲染 */}
        {renderFilter ? renderFilter({ search: this.handleSearch }) : null}
        {/* 自定义列表项渲染 */}
        <div className="grid-list">
          {(gridData?.list || []).map((item, index) => renderItem(item, index))}
        </div>
        {/* 加载更多触发区域 */}
        <div ref={this.loadMoreRef} onClick={this.loadMore}>
          {gridData?.hasMore ? '加载更多' : '没有更多数据'}
        </div>
      </div>
    )
  }
}

const mapStateToProps = (state, ownProps) => ({
  gridData: state.grid[ownProps.namespace] || {}
})

export default connect(mapStateToProps, { gridSearchAction, gridLoadMoreAction })(BaseGrid);

3. 页面使用示例

每个页面仅需要传入配置参数,不需要写重复逻辑:

// 业务页面示例
import BaseGrid from 'src/common/grid/BaseGrid';

class UnitPage extends Component {
  render() {
    return (
      <BaseGrid
        {/* 唯一命名空间,区分不同页面的状态 */}
        namespace="unit_manage_list"
        {/* 基础搜索参数 */}
        baseSearchParams={{ statusId: 1, pageSize: 20 }}
        {/* 自定义参数处理,适配页面特殊需求 */}
        customParamHandler={(params) => {
          return {...params, bizType: 'unit'}
        }}
        {/* 自定义筛选栏UI */}
        renderFilter={({search}) => (
          <div className="filter-bar">
            <input placeholder="输入单元名称" onChange={e => search({name: e.target.value})}/>
            <button onClick={() => search()}>搜索</button>
          </div>
        )}
        {/* 自定义列表项UI */}
        renderItem={(item) => (
          <div key={item.id} className="grid-item">
            <span>{item.unitName}</span>
            <span>{item.createTime}</span>
          </div>
        )}
      />
    )
  }
}

方案收益

  • 所有列表相关逻辑仅需要维护公共层的3个文件,bug修复、功能迭代只需要修改一次全页面生效
  • 每个业务页面代码量减少70%以上,仅需要写配置和UI渲染逻辑
  • Redux逻辑完全统一,不会出现各组件实现不一致的问题
  • 扩展性强,特殊页面可以通过传入自定义处理函数覆盖默认逻辑,不需要修改公共层代码

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.20 12:45:00