Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] 是否有计划支持 Antd Table rowSelection #2471

Open
raotaohub opened this issue Feb 6, 2024 · 11 comments
Open

[Feature] 是否有计划支持 Antd Table rowSelection #2471

raotaohub opened this issue Feb 6, 2024 · 11 comments
Assignees

Comments

@raotaohub
Copy link

raotaohub commented Feb 6, 2024

请问目前是否有计划支持 Antd Table 组件的 rowSelection属性通过 hook

  1. 单独新建立一个 useTableSelections 模块 (目前已有useSelections)
  2. 或是基于 useAntdTable 通过第2个参数拓展 (是否有开销问题,和适用性不够广的问题,或是说这个能力不应该在该hooks中)

下面是我目前在使用的实现

基于方案1的实现

import React, { useMemo, useState } from 'react';
import { useMemoizedFn } from 'ahooks';

type RowSelectionType = 'checkbox' | 'radio';

type GetRowKey<RecordType> = (record: RecordType, index?: number) => React.Key;

interface TableRowSelection<RecordType> {
  type: RowSelectionType;
  selectedRowKeys: React.Key[];
  defaultSelectedRowKeys: React.Key[];
  getCheckboxProps: (row: RecordType) => { disabled: boolean };
  onChange: (selectedRowKeys: React.Key[], selectedRows: RecordType[]) => void;
  rowKey: GetRowKey<RecordType>;
  disabled: boolean | ((row: RecordType) => boolean);
}

export interface AntdTableSelections<RecordType> {
  state: {
    allSelected: boolean;
    selectedRows: RecordType[];
    selectedRowKeys: React.Key[];
  };
  action: {
    toggle: (item: RecordType) => void;
    toggleAll: () => void;
    selectAll: () => void;
    unSelectAll: () => void;
    setSelected: (keys: React.Key[]) => void;
  };
  rowSelection: Omit<TableRowSelection<RecordType>, 'rowKey' | 'disabled'>;
}

function useTableSelections<RecordType>(
  rows: RecordType[],
  config?: Partial<TableRowSelection<RecordType>>
): AntdTableSelections<RecordType> {
  const {
    type = 'checkbox',
    rowKey = 'key',
    defaultSelectedRowKeys = [],
    disabled = false,
    ...rest
  } = config || {};

  const [selectedRows, setSelectedRows] = useState<RecordType[]>(rows || []);
  const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>(defaultSelectedRowKeys);

  // ========================= Keys =========================
  const getRowKey = React.useMemo<GetRowKey<RecordType>>(() => {
    if (typeof rowKey === 'function') {
      return rowKey;
    }

    return (record: RecordType) => (record as any)?.[rowKey];
  }, [rowKey]);

  // ========================= Funcs =========================
  const onRowsChange = useMemoizedFn((rows: RecordType[]) => {
    setSelectedRows(rows);
    setSelectedRowKeys(rows.map((t) => getRowKey(t)));
  });

  const onSelectionChange = useMemoizedFn(
    (selectedRowKeys: React.Key[], selectedRows: RecordType[]) => {
      setSelectedRows(selectedRows);
      setSelectedRowKeys(selectedRowKeys);
    }
  );

  const getCheckboxProps = useMemoizedFn((row: RecordType) => {
    if (typeof disabled === 'boolean') {
      return { disabled };
    }

    if (typeof disabled === 'function') {
      return { disabled: disabled(row) };
    }

    return { disabled: false };
  });

  // ========================= Select Set =========================
  const selectedSet = useMemo(() => new Set(selectedRows), [selectedRows]);

  const isSelected = (item: RecordType) => selectedSet.has(item);

  const unSelect = (item: RecordType) => {
    selectedSet.delete(item);
    onRowsChange(Array.from(selectedSet));
  };

  const selectAll = () => {
    rows.forEach((o) => {
      selectedSet.add(o);
    });

    onRowsChange(Array.from(selectedSet));
  };

  const unSelectAll = () => {
    rows.forEach((o) => {
      selectedSet.delete(o);
    });
    onRowsChange(Array.from(selectedSet));
  };

  const select = (item: RecordType) => {
    selectedSet.add(item);
    onRowsChange(Array.from(selectedSet));
  };

  const toggle = (item: RecordType) => {
    if (isSelected(item)) {
      unSelect(item);
    } else {
      select(item);
    }
  };

  const noneSelected = useMemo(() => rows.every((o) => !selectedSet.has(o)), [rows, selectedSet]);

  const allSelected = useMemo(() => rows.every((o) => selectedSet.has(o)) && !noneSelected, [
    rows,
    selectedSet,
    noneSelected,
  ]);

  const toggleAll = () => (allSelected ? unSelectAll() : selectAll());

  const setSelected = useMemoizedFn((keys: React.Key[]) => setSelectedRowKeys(keys));

  return {
    state: {
      allSelected,
      selectedRows,
      selectedRowKeys,
    },
    action: {
      toggle,
      toggleAll,
      selectAll,
      unSelectAll,
      setSelected,
    },
    rowSelection: {
      ...rest,
      type,
      onChange: onSelectionChange,
      getCheckboxProps,
      selectedRowKeys,
      defaultSelectedRowKeys,
    },
  };
}

export default useTableSelections;

基于以上方案1的使用

配合 useAntdTable 获得 tableProps.dataSource

  const { loading, tableProps, search } = useAntdTable(getTableData, {
    form,
  });
  
  const { state, action, rowSelection } = useTableSelections<IProject>(tableProps.dataSource, {
    rowKey: (row) => row.guid,
    disabled: (row) => row.diabled || row.name === 'foo',
  });


  return (
        <Table
        {...tableProps}
        rowSelection={rowSelection}
        columns={columns}
        rowKey={(row) => row.guid}
      />
  )
@raotaohub
Copy link
Author

raotaohub commented Feb 6, 2024

之所以上面的代码没有直接使用 useSelections ,是因为需要同时关注 rows 和 rowKeys 所以代码成本差不多。
上面的代码一个简易示例,完整版已通过pr提交#2477

@raotaohub raotaohub changed the title [Feature] 是否有计划支持 Antd Table rowSelections [Feature] 是否有计划支持 Antd Table rowSelection Feb 9, 2024
@raotaohub
Copy link
Author

video example

2024-02-09.10.01.47.mov

@crazylxr
Copy link
Collaborator

@hchlq 你这边先调研一下呢

@hchlq
Copy link
Collaborator

hchlq commented Feb 28, 2024

这个 hooks 使用场景好像不是很多呢?

@liuyib
Copy link
Collaborator

liuyib commented Feb 28, 2024

@raotaohub 需要加这个 useAntdTableSelection hook 是不是因为 useSelections 不支持对象数组,导致你 useSelections 和 Table 结合起来很麻烦?

我前几天也遇到这种需要自己实现跨页选中的场景(一般组件库内置,但也会有需要手动实现的场景),使用 useSelections 实现不了,所以我改进了 useSelections: #2485 (这个 PR 的改进我已经投入实际使用了,完全能 cover 住复杂场景的跨页选择)。

所以,我的理解,如果 useSelections 支持对象数组,这种跨页多选的场景(Table 跨页多选、List 跨页多选 等),都直接覆盖了。

@liuyib
Copy link
Collaborator

liuyib commented Feb 28, 2024

@raotaohub 你可以把这个 PR 的代码 copy 到你本地,新建一个临时 hook 文件粘进去(一些报错,缺失的工具方法,用 lodash-es 替换掉)。试试 useSelections 支持了对象数组后,是不是就满足你的需求了?

我不太建议新增 useAntdTableSelection 这个 hook 哈,要不然 Table 要加个 useAntdTableSelection、List 要加个 useListSelection、......

@raotaohub
Copy link
Author

@raotaohub 需要加这个 useAntdTableSelection hook 是不是因为 useSelections 不支持对象数组,导致你 useSelections 和 Table 结合起来很麻烦?

我前几天也遇到这种需要自己实现跨页选中的场景(一般组件库内置,但也会有需要手动实现的场景),使用 useSelections 实现不了,所以我改进了 useSelections: #2485 (这个 PR 的改进我已经投入实际使用了,完全能 cover 住复杂场景的跨页选择)。

所以,我的理解,如果 useSelections 支持对象数组,这种跨页多选的场景(Table 跨页多选、List 跨页多选 等),都直接覆盖了。

是的,原有的useSelections确实无法覆盖 Table 在 rowSelection

另外还有一些原因

我考虑过改进useSelections,但是我翻了antd Table 在 rowSelection涉及逻辑中,使用到的单选,多选等逻辑,和已有的Checkbox组件(已被useSelections所支持)其实不完全一样 。

具体如 Checkbox 不关注对象数组,不关注数组对象的rowKey,不可切换为radio形态,而这在Table的rowSelection是需要的。

@raotaohub
Copy link
Author

@raotaohub 你可以把这个 PR 的代码 copy 到你本地,新建一个临时 hook 文件粘进去(一些报错,缺失的工具方法,用 lodash-es 替换掉)。试试 useSelections 支持了对象数组后,是不是就满足你的需求了?

我不太建议新增 useAntdTableSelection 这个 hook 哈,要不然 Table 要加个 useAntdTableSelection、List 要加个 useListSelection、......

copy 到你本地

我会尝试copy 到你本地运行看看。目前我的项目中已经在使用 useAntdTableSelection 我会进行对比,看看是否有更好的方案

@raotaohub
Copy link
Author

@raotaohub 你可以把这个 PR 的代码 copy 到你本地,新建一个临时 hook 文件粘进去(一些报错,缺失的工具方法,用 lodash-es 替换掉)。试试 useSelections 支持了对象数组后,是不是就满足你的需求了?

我不太建议新增 useAntdTableSelection 这个 hook 哈,要不然 Table 要加个 useAntdTableSelection、List 要加个 useListSelection、......

对于useAntdTable这个独立hook而言是只关注table和请求的相关的,而Table组件足够强大和复杂,单个hooks的出现是为了覆盖真正的高频场景。

我和你有一样的担忧,倘若每次都要针对Table一些功能再建立一个独立hook, 可能会起一个不好的头。这点还是需要看看这个功能是否足够高频和通用。crazylxr 正如他所说,应该调研看看~

之所以最终还是提出了这个issue和pr,是考虑到在大量2b业务中和个别业务领域里,这个功能也是相当高频

@liuyib
Copy link
Collaborator

liuyib commented Feb 28, 2024

不可切换为radio形态,而这在Table的rowSelection是需要的

这个有点疑惑,Table 中 radio 貌似没有意义啊?能否给出使用场景 @raotaohub

@raotaohub
Copy link
Author

不可切换为radio形态,而这在Table的rowSelection是需要的

这个有点疑惑,Table 中 radio 貌似没有意义啊?能否给出使用场景 @raotaohub

实际业务中的确不常见,这里我是举个例子说明 useSelections 和 Table 的 rowSelection的差异(这里支持type为radio)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants