使用Copilot优化Select全选组件

129 阅读5分钟

优化前代码

import React, { useState, useEffect } from 'react';
import { Select, Checkbox, Divider } from 'antd';

interface Option {
  label: string;
  value: string | number;
}

interface SelectAllProps {
  defaultValue: (string | number)[];
  options: Option[];
  width?: string;
  onChange: (params: { selectData: (string | number)[] }) => void;
}

const SelectAll: React.FC<SelectAllProps> = (props) => {
  const { defaultValue, options, width, onChange } = props;

  const [status, setStatus] = useState<boolean>(false); // 全选状态
  const [selectData, setSelectData] = useState<(string | number)[]>([]); // 当前选中值

  useEffect(() => {
    if (defaultValue.length) {
      setSelectData(defaultValue);
      setStatus(defaultValue.length === options.length); // 判断是否全选
    }
  }, [defaultValue, options]);

  const handleSelectChange = (value: (string | number)[]) => {
    setSelectData(value);
    setStatus(value.length === options.length); // 更新全选状态
    onChange({ selectData: value });
  };

  const handleSelectAll = (checked: boolean) => {
    if (checked) {
      const allValues = options.map((item) => item.value);
      setSelectData(allValues);
      setStatus(true);
      onChange({ selectData: allValues });
    } else {
      setSelectData([]);
      setStatus(false);
      onChange({ selectData: [] });
    }
  };

  return (
    <Select
      placeholder="请选择"
      style={{ width: width || '300px' }}
      mode="multiple"
      maxTagCount="responsive"
      allowClear
      showSearch
      value={selectData}
      options={options}
      filterOption={(input, option) =>
        option?.label.toLowerCase().includes(input.toLowerCase()) ||
        option?.value.toString().includes(input)
      }
      onChange={handleSelectChange}
      dropdownRender={(menu) => (
        <>
          <div style={{  cursor: 'pointer' }}>
            <Checkbox
              checked={status}
              onChange={(e) => handleSelectAll(e.target.checked)}
            >
              全选
            </Checkbox>
          </div>
          <Divider style={{ margin: '0' }} />
          {menu}
        </>
      )}
    />
  );
};

export default SelectAll;

优化前,存在的问题

筛选后点击全选,选择的是所有列表,而不是过滤后的列表。

AI优化后

要优化过滤时的全选功能,使其仅选择过滤后的值,可以在 filterOptiononChange 的逻辑中动态更新全选的选项。以下是优化后的代码:


优化后的代码

import React, { useState, useEffect } from 'react';
import { Select, Checkbox, Divider } from 'antd';

interface SelectAllProps {
  defauleValue: any[];
  options: any[];
  width?: string;
  onChange: (params: any) => void;
}

const SelectAll: React.FC<SelectAllProps> = (props) => {
  const { defauleValue, options, width, onChange } = props;

  const [status, setStatus] = useState<boolean>(false); // 全选状态
  const [selectData, setSelectData] = useState<any[]>([]); // 当前选中的值
  const [filteredOptions, setFilteredOptions] = useState<any[]>(options); // 当前过滤后的选项

  useEffect(() => {
    if (defauleValue.length) {
      setSelectData(defauleValue);
      if (defauleValue.length === options.length) {
        setStatus(true);
      }
    }
  }, [defauleValue, options]);

  const handleSelectChange = (value: any[], option: any[]) => {
    setSelectData(value);
    onChange({
      selectData: value,
      selectOption: option,
    });
    setStatus(value.length === filteredOptions.length); // 判断是否全选
  };

  const handleSelectAll = (checked: boolean) => {
    if (checked) {
      const allValues = filteredOptions.map((item) => item.value);
      setSelectData(allValues);
      setStatus(true);
      onChange({
        selectData: allValues,
        selectOption: filteredOptions,
      });
    } else {
      setSelectData([]);
      setStatus(false);
      onChange({
        selectData: [],
        selectOption: [],
      });
    }
  };

  const handleFilter = (input: string) => {
    const filtered = options.filter(
      (option) =>
        option.label.toLowerCase().includes(input.toLowerCase()) ||
        option.value.toString().includes(input)
    );
    setFilteredOptions(filtered); // 更新过滤后的选项
  };

  return (
    <Select
      placeholder="请选择"
      style={{ width: width || '300px' }}
      mode="multiple"
      maxTagCount="responsive"
      allowClear
      showSearch
      value={selectData}
      options={filteredOptions}
      filterOption={false} // 自定义过滤逻辑
      onSearch={handleFilter} // 处理搜索
      onChange={handleSelectChange}
      dropdownRender={(menu) => (
        <>
          <div style={{ padding: '4px 8px 8px 12px', cursor: 'pointer' }}>
            <Checkbox
              checked={status}
              onChange={(e) => handleSelectAll(e.target.checked)}
            >
              全选
            </Checkbox>
          </div>
          <Divider style={{ margin: '0' }} />
          {menu}
        </>
      )}
    />
  );
};

export default SelectAll;

优化点说明

  1. 动态更新过滤后的选项:

    • 使用 onSearchsetFilteredOptions 动态更新当前过滤后的选项。
  2. 全选仅选择过滤后的值:

    • 在全选逻辑中,使用 filteredOptions 而不是 options,确保全选仅选择当前过滤后的值。
  3. 自定义过滤逻辑:

    • filterOption 设置为 false,并通过 onSearch 手动实现过滤逻辑。
  4. 全选状态更新:

    • 根据 filteredOptions 的长度和当前选中值的长度动态更新全选状态。


效果

  1. 搜索过滤:

    • 输入关键字时,选项会动态过滤。
  2. 全选功能:

    • 全选仅选择当前过滤后的选项。
  3. 状态同步:

    • 全选状态会根据当前过滤后的选项和选中值动态更新。

AI优化后,存在的问题

再次打开select全选组件,下拉选项没有重置为全部,导致已选择项显示异常。

人工再次优化后,AI分析

ASK Copilot: 分析在你的建议之后,我又优化了什么

在你的代码中,我注意到你已经实现了一些优化,以下是我对你的优化的分析和总结:


1. 使用 Set 去重

你在 onChange 的逻辑中使用了 Set 来对数组进行去重:

newData = Array.from(new Set([...seletData, ...a]));
  • 优化点: 通过 Set 去重,避免了重复值的出现,简化了去重逻辑。
  • 改进效果: 代码更加简洁,性能更高,避免了手动遍历数组去重的复杂性。

2. 动态更新 filteredOptions

你在 onSearchonDropdownVisibleChange 中动态更新了 filteredOptions

onSearch={(value) => {
  const filtered = options.filter((option) => {
    return (
      option.label.toLowerCase().includes(value.toLowerCase()) ||
      option.value.toString().includes(value)
    );
  });
  setFilteredOptions(filtered);
  setStatus(
    filtered.filter((item) => seletData.includes(item.value)).length ===
      filtered.length
  );
}}
  • 优化点:
    • 在搜索时动态过滤选项。
    • 在下拉面板展开时重置 filteredOptions 为原始的 options
  • 改进效果:
    • 搜索功能更加灵活,支持动态过滤。
    • 在面板关闭后,选项列表能够恢复到初始状态。

3. 全选逻辑优化

你在 CheckboxonChange 中实现了全选逻辑,并动态更新状态:

onChange={(e) => {
  const a = filteredOptions.map((item) => item.value);
  let newData: any[] = [];
  if (e.target.checked) {
    newData = Array.from(new Set([...seletData, ...a]));
  } else {
    newData = seletData.filter((item: any) => !a.includes(item));
  }
  const newOptions = options.filter((item) => newData.includes(item.value));
  setSelectData(newData);
  setSelectOption(newOptions);
  setStatus(newData.length === options.length);
  onChange({
    seletData: newData,
    selectOption: newOptions,
  });
}}
  • 优化点:
    • 全选逻辑基于 filteredOptions,确保全选仅作用于当前过滤后的选项。
    • 使用 Set 去重,避免重复值。
    • 动态更新 status,确保全选状态与选中值同步。
  • 改进效果:
    • 全选功能更加智能,支持过滤后的全选。
    • 状态同步更加准确,避免了逻辑错误。

4. 状态同步

你在多个地方动态更新了 status,确保全选状态与选中值保持一致:

setStatus(newData.length === options.length);
  • 优化点:
    • onChangeonSearchonDropdownVisibleChange 中动态更新 status
  • 改进效果:
    • 全选状态能够实时反映当前选中值的状态,用户体验更好。

5. 代码结构清晰

你将逻辑分散到不同的事件处理函数中,例如 onSearchonChangeonDropdownVisibleChange,使代码结构更加清晰。

  • 优化点:
    • 每个事件处理函数只负责一个功能,职责单一。
  • 改进效果:
    • 代码可读性更高,便于维护和扩展。

总结

你的优化主要体现在以下几个方面:

  1. 使用 Set 去重: 简化了去重逻辑,提升了性能。
  2. 动态更新 filteredOptions: 支持搜索过滤,并在面板关闭时恢复初始状态。
  3. 全选逻辑优化: 全选功能更加智能,支持过滤后的全选。
  4. 状态同步: 确保全选状态与选中值保持一致。
  5. 代码结构清晰: 逻辑分工明确,代码更易读。