实现useSelections

415 阅读2分钟

自定义hooks

作为react的使用者,自定义hooks是常见的业务需求,小弟技术水平有限,只能先借花献佛聊下公司里大佬们封装的自定义hooks了

import { useMemo, useState } from 'react';
import { useMemoizedFn } from '@pansy/use-memoized-fn';

export type ItemVal = string | number;

/**
 * 常见联动 Checkbox 逻辑封装
 * @param items 所有可选项
 * @param defaultSelected 默认选中的项
 * @returns
 */
export function useSelections<ItemVal>(
  items: ItemVal[],
  defaultSelected: ItemVal[] = []
) {
  const [selected, setSelected] = useState<ItemVal[]>(defaultSelected);

  const selectedSet = useMemo(() => new Set(selected), [selected]);

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

  const select = (item: ItemVal) => {
    selectedSet.add(item);
    return setSelected(Array.from(selectedSet));
  };

  const unSelect = (item: ItemVal) => {
    selectedSet.delete(item);
    return setSelected(Array.from(selectedSet));
  };

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

  const selectAll = () => {
    items.forEach((o) => {
      selectedSet.add(o);
    });
    setSelected(Array.from(selectedSet));
  };

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

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

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

  const partiallySelected = useMemo(
    () => !noneSelected && !allSelected,
    [noneSelected, allSelected],
  );

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

  return {
    selected,
    noneSelected,
    allSelected,
    partiallySelected,
    setSelected,
    isSelected,
    select: useMemoizedFn(select),
    unSelect: useMemoizedFn(unSelect),
    toggle: useMemoizedFn(toggle),
    selectAll: useMemoizedFn(selectAll),
    unSelectAll: useMemoizedFn(unSelectAll),
    toggleAll: useMemoizedFn(toggleAll),
  } as const;
}

常见联动 Checkbox 逻辑封装,支持多选,单选,全选逻辑,还提供了是否选择,是否全选,是否半选的状态。

用法如下

基础用法

import { Checkbox, Col, Row } from 'antd';
import React, { useMemo, useState } from 'react';
import { useSelections } from '@pansy/react-hooks';

export default () => {
  const [hideOdd, setHideOdd] = useState(false);

  const list = useMemo(() => {
    if (hideOdd) {
      return [2, 4, 6, 8];
    }
    return [1, 2, 3, 4, 5, 6, 7, 8];
  }, [hideOdd]);

  const { selected, allSelected, isSelected, toggle, toggleAll, partiallySelected } = useSelections(
    list,
    [1],
  );

  return (
    <div>
      <div>Selected : {selected.join(',')}</div>
      <div style={{ borderBottom: '1px solid #E9E9E9', padding: '10px 0' }}>
        <Checkbox checked={allSelected} onClick={toggleAll} indeterminate={partiallySelected}>
          Check all
        </Checkbox>
        <Checkbox checked={hideOdd} onClick={() => setHideOdd((v) => !v)}>
          Hide Odd
        </Checkbox>
      </div>
      <Row style={{ padding: '10px 0' }}>
        {list.map((o) => (
          <Col span={12} key={o}>
            <Checkbox checked={isSelected(o)} onClick={() => toggle(o)}>
              {o}
            </Checkbox>
          </Col>
        ))}
      </Row>
    </div>
  );
}

进阶用法

import { Checkbox, Col, Row } from 'antd';
import React, { useMemo, useState } from 'react';
import { useSelections } from '@pansy/react-hooks';

const data1 = [
  { label: 'item1', value: 1 },
  { label: 'item2', value: 2 },
  { label: 'item3', value: 3 },
  { label: 'item4', value: 4 },
  { label: 'item5', value: 5 },
  { label: 'item6', value: 6 },
  { label: 'item7', value: 7 },
  { label: 'item8', value: 8 },
];

const data2 = [
  { label: 'item2', value: 2 },
  { label: 'item4', value: 4 },
  { label: 'item6', value: 6 },
  { label: 'item8', value: 8 },
];

export default () => {
  const [hideOdd, setHideOdd] = useState(false);

  const list = useMemo(() => {
    if (hideOdd) {
      return data2;
    }
    return data1;
  }, [hideOdd]);

  const items = useMemo(() => {
    return list.map(item => item.value);
  }, [list]);

  const { selected, allSelected, isSelected, toggle, toggleAll, partiallySelected } = useSelections(
    items,
    [1],
  );

  return (
    <div>
      <div>Selected : {selected.join(',')}</div>
      <div style={{ borderBottom: '1px solid #E9E9E9', padding: '10px 0' }}>
        <Checkbox checked={allSelected} onClick={toggleAll} indeterminate={partiallySelected}>
          Check all
        </Checkbox>
        <Checkbox checked={hideOdd} onClick={() => setHideOdd((v) => !v)}>
          Hide Odd
        </Checkbox>
      </div>
      <Row style={{ padding: '10px 0' }}>
        {list.map((o, index) => (
          <Col span={12} key={index}>
            <Checkbox checked={isSelected(o.value)} onClick={() => toggle(o.value)}>
              {o.label}
            </Checkbox>
          </Col>
        ))}
      </Row>
    </div>
  );
}

分页

import React, { useMemo, useState } from 'react';
import { Checkbox, Col, Row, Space, Pagination } from 'antd';
import { useSelections } from '@pansy/react-hooks';

const list = [
  { label: 'item01', value: 1 },
  { label: 'item02', value: 2 },
  { label: 'item03', value: 3 },
  { label: 'item04', value: 4 },
  { label: 'item05', value: 5 },
  { label: 'item06', value: 6 },
  { label: 'item07', value: 7 },
  { label: 'item08', value: 8 },
  { label: 'item09', value: 9 },
  { label: 'item10', value: 10 },
  { label: 'item11', value: 11 },
  { label: 'item12', value: 12 },
  { label: 'item13', value: 13 },
  { label: 'item14', value: 14 },
  { label: 'item15', value: 15 },
  { label: 'item16', value: 16 },
];

export default () => {
  const [currentPage, setCurrentPage] = useState(1);
  const [pageSize] = useState(8);

  const dataSource = useMemo(() => {
    return list
      .slice((currentPage - 1) * pageSize, currentPage * pageSize);
  }, [currentPage, pageSize]);

  const items = dataSource.map(item => item.value)

  const {
    selected,
    allSelected,
    partiallySelected,
    isSelected,
    toggle,
    toggleAll,
  } = useSelections<number>(items, [1]);

  return (
    <div>
      <div style={{ borderBottom: '1px solid #E9E9E9', padding: '10px 0' }}>
        <Space>
          <Checkbox checked={allSelected} onClick={toggleAll} indeterminate={partiallySelected}>
            全选
          </Checkbox>

          <span>已选中 {selected.length} 项</span>
        </Space>
      </div>

      <Row style={{ padding: '10px 0' }}>
        {dataSource.map((o, index) => (
          <Col span={12} key={index}>
            <Checkbox checked={isSelected(o.value)} onClick={() => toggle(o.value)}>
              {o.label}
            </Checkbox>
          </Col>
        ))}
      </Row>

      <div style={{ paddingTop: 16 }}>
        <Pagination
          size="small"
          style={{
            float: 'right'
          }}
          total={list.length}
          onChange={(page) => {
            setCurrentPage(page);
          }}
          showTotal={(tptal) => {
            return `共 ${tptal} 项`;
          }}
        />
      </div>
    </div>
  );
}

个人的分享记录,不喜勿喷。