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

React自研网格单列排序问题:点击第二列时首列排序图标消失

自研React网格组件多列排序异常问题

我正在为自研React网格组件添加排序功能,未使用任何第三方库构建网格及实现排序。当前组件仅支持单列排序:当尝试为两列启用排序功能后,点击第二列排序时,第一列的排序图标会消失。我正在排查排序实现逻辑,但无法定位问题根源。

排序功能核心源码

export const ORDER = {
  DESC: 'desc',
  ASC: 'asc'
};

export class SortInfo {
  constructor({ columnId, order }) {
    this.columnId = columnId;
    this.order = order;
  }

  getColumnId() {
    return this.columnId;
  }

  setOrder(order) {
    return new SortInfo({ ...this, order });
  }

  getOrder() {
    return this.order === ORDER.DESC ? ORDER.DESC : ORDER.ASC;
  }

  isValid(gridData) {
    return gridData.getHeaders().indexOf(this.columnId) >= 0;
  }

  isEmpty() {
    return this.columnId === undefined || this.columnId === null || this.columnId === '';
  }

  isEqual(anotherSortInfo) {
    return (
      anotherSortInfo &&
      this.getColumnId() === anotherSortInfo.getColumnId() &&
      this.getOrder() === anotherSortInfo.getOrder()
    );
  }

  serialize() {
    const { columnId, order } = this;
    return { columnId, order };
  }
}

function createSortInfo(columnId, order) {
  return new SortInfo({ columnId, order });
}

export function createAscendingSortInfo(columnId) {
  return createSortInfo(columnId, ORDER.ASC);
}

export function createDescendingSortInfo(columnId) {
  return createSortInfo(columnId, ORDER.DESC);
}

SortInfo的使用代码

function createGridHeaders(gridData, onSort) {
  const sortInfo = gridData.getSortInfo();
  const sortId = sortInfo ? sortInfo.getColumnId() : undefined;
  gridData.getHeaders().map(id => ({headerName: new FieldRef(id, getGlobal).getLabel()}))
  return gridData
    .getHeaders()
    .map(id => ({
      headerName: new FieldRef(id, getGlobal).getLabel(),
      field: id,
      cellRendererParams: { format: getCellFormatter(new FieldRef(id, getGlobal)) },
      resizable: true,
      headerComponentParams: {
        sortOrder: id === sortId ? sortInfo.getOrder() : undefined
      },
      onSort
    }));
}
    const columns = gridData.getHeaders().length > 0
          ? createGridHeaders(gridData, this.onSort)
          : undefined;
return (
      <GridPage
        className="sampleTable"
        title={title}
        subtitle={subtitle}
        buttons={gridButtons}
        onLoad={this.fetchData}
        gridData={gridData}
        columns={columns}
      >
      </GridPage>
    );

问题根源分析

  • SortInfo仅支持单列存储:当前SortInfo类只能保存单个列的排序信息,点击第二列时,新的SortInfo会直接覆盖旧的,导致第一列的排序状态完全丢失。
  • 表头渲染依赖单一列ID:在createGridHeaders中,仅通过sortId判断当前列是否显示排序图标,其他列直接设为undefined,切换列后之前的列自然失去图标。
  • 排序触发逻辑无状态累积:虽然未展示onSort实现,但可以推测点击新列时是直接替换整个SortInfo对象,而非在已有排序状态基础上添加新列的排序规则。

修复方案

1. 改造SortInfo为多列排序存储结构

将单例的SortInfo改为支持多列排序的集合结构:

export const ORDER = {
  DESC: 'desc',
  ASC: 'asc',
  NONE: 'none'
};

export class MultiSortInfo {
  constructor(sortEntries = []) {
    this.sortEntries = sortEntries;
  }

  getOrder(columnId) {
    const entry = this.sortEntries.find(item => item.columnId === columnId);
    return entry?.order || ORDER.NONE;
  }

  toggleSort(columnId) {
    const currentOrder = this.getOrder(columnId);
    let newOrder;
    switch(currentOrder) {
      case ORDER.NONE:
        newOrder = ORDER.ASC;
        break;
      case ORDER.ASC:
        newOrder = ORDER.DESC;
        break;
      case ORDER.DESC:
        newOrder = ORDER.NONE;
        break;
    }
    const updatedEntries = newOrder !== ORDER.NONE 
      ? [...this.sortEntries.filter(item => item.columnId !== columnId), {columnId, order: newOrder}]
      : this.sortEntries.filter(item => item.columnId !== columnId);
    return new MultiSortInfo(updatedEntries);
  }

  getSortEntries() {
    return [...this.sortEntries];
  }

  serialize() {
    return this.sortEntries;
  }
}

2. 更新表头渲染逻辑

针对每列单独查询排序状态,不再依赖单一sortId:

function createGridHeaders(gridData, onSort) {
  const multiSortInfo = gridData.getSortInfo();
  return gridData
    .getHeaders()
    .map(id => ({
      headerName: new FieldRef(id, getGlobal).getLabel(),
      field: id,
      cellRendererParams: { format: getCellFormatter(new FieldRef(id, getGlobal)) },
      resizable: true,
      headerComponentParams: {
        sortOrder: multiSortInfo.getOrder(id) !== ORDER.NONE ? multiSortInfo.getOrder(id) : undefined
      },
      onSort: () => onSort(id)
    }));
}

3. 修改排序触发逻辑

调用MultiSortInfo的toggleSort方法累积排序状态,而非替换整个对象:

onSort = (columnId) => {
  const currentMultiSort = this.state.gridData.getSortInfo();
  const newMultiSort = currentMultiSort.toggleSort(columnId);
  
  // 更新排序信息
  this.setState(prevState => ({
    gridData: {...prevState.gridData, sortInfo: newMultiSort}
  }));
  
  // 根据多列排序规则重新排序数据
  const sortedData = this.sortGridData(this.state.gridData.getData(), newMultiSort);
  this.setState({ gridData: {...this.state.gridData, data: sortedData} });
}

sortGridData(data, multiSortInfo) {
  const sortEntries = multiSortInfo.getSortEntries();
  if (sortEntries.length === 0) return data;
  
  return [...data].sort((a, b) => {
    for (const {columnId, order} of sortEntries) {
      const valA = a[columnId];
      const valB = b[columnId];
      if (valA < valB) return order === ORDER.ASC ? -1 : 1;
      if (valA > valB) return order === ORDER.ASC ? 1 : -1;
    }
    return 0;
  });
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 02:47:13