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
相关产品推荐
相关产品推荐

