【react】全选复选框和单个复选框联动功能

需求:

  1. 全选复选框 显示情况:点击全选复选框时,所有单个复选框状态变为被勾选状态;并且每一单个复选框边框颜色为蓝色;当点击取消全选复选框被勾选的状态时,所有单个复选框状态恢复初始状态(未被勾选状态)。
  2. 单个复选框 显示情况:点击某个复选框(单个)时,全选复选框状态为半选状态;将所有单个复选框都点击时,全选复选框状态为被勾选状态。当取消其中一个被勾选状态的复选框(单个)全选复选框状态变为半选状态,当所有单个复选框被勾选状态都被取消时,全选复选框的状态变为初始状态(未被勾选状态)。

在index.tsx中

const PosterList: React.FC = () => {
  const [dataList, setDataList] = useState([] as any[]); // 数据
  const [checkAll, setCheckAll] = React.useState(false); // 全选复选框
  const [indeterminate, setIndeterminate] = React.useState(false); // 半选状态
  const [checkedList, setCheckedList] = useState([] as any[]); // 单个复选框

  const selectedData = [] // 是否被选中的数据,此数据调用接口获得
  
  // 数据列表接口
  const { data } = useRequest(getList, {
    defaultParams: [
      {
        start: 0,
        length: 50,
        searchMap: {
          loginAccount: '',
          title: '',
          configId: '',
        },
      },
    ],
  });
  
  // 初始进来如果有选中的值,则为checkedList添加数据
  useEffect(() => {
    if (selectedData) {
      const list = selectedData
        .filter((item) => item.type === 4)
        .map((item) => {
          const id = item.content.id || item.content.configId;
          return { ...item.content, id };
        });
      setCheckedList(list);
    }
  }, [selectedData]);
  
  // 初始为列表数据添加check
  useEffect(() => {
    if (data && data.data) {
      data.data.forEach((item: { check: boolean }) => {
        item.check = false;
      });
      setDataList(data.data);
    }
  }, [data]);
  
  // 判断是全选还是半选的逻辑
  useEffect(() => {
    const checkedListLength = checkedList.length;
    // 如果checkedListLength 为0则为选择
    if (checkedListLength === 0) {
      setCheckAll(false);
      setIndeterminate(false);
    } else {
      setCheckAll(true);
      // 判断全选还是半选
      setIndeterminate(dataList.some(
          (item) => !checkedList.find((items) => items.id === item.id))
      );
    }
  }, [dataList, checkedList.length, checkedList]);
  
  // 单个复选框变化
  const onChangeCheck = (e: React.MouseEvent<HTMLElement, MouseEvent>, it: AnyIfEmpty<object>) => {
    e.preventDefault();
    e.stopPropagation();
    // 存储数据当前数据到新数组中
    const lists: AnyIfEmpty<object> = [...dataList];
    // 将当前的单选框点击变为非
    const index = checkedList.findIndex((items) => items.id === it.id);
    if (index > -1) {
      checkedList.splice(index, 1);
      setCheckedList(checkedList);
    } else {
      setCheckedList([...checkedList, it]);
    }
    setDataList(lists);
  };
  
  // 全选复选框
  const onCheckAllChange = (e: React.MouseEvent<HTMLElement, MouseEvent>) => {
    // 所有数据保存到 lists中
    const lists = [...dataList];
    // 定义 flag 判断是否为true
    const flag = lists.every((item: { check: boolean }) => item.check === true);
    // 遍历lists的 check是否为true
    lists.forEach((item: { check: boolean }) => {
      item.check = !flag;
    });
    if (e.target.checked) {
      setCheckedList([...lists]);
    } else {
      setCheckedList([]);
    }
    setDataList(lists);
  };
  
  return (<>
	<div className="pagination">
		<AllCheckBox
			indeterminate={indeterminate}
			onChange={onCheckAllChange}
			checked={checkAll}
		>
			所有数据
		</AllCheckBox>
	</div>
    <div className="poster">
        {dataList.map((item) => (
            <div key={item.id} className="posterListouter">
              <Div
                className="posterList"
                clickId={checkedList.find((items) => items.id === item.id)}
                onClick={(e) => onChangeCheck(e, item)}
              >
                <Checkbox
                  onClick={(e) => onChangeCheck(e, item)}
                  checked={checkedList.find((items) => items.id === item.id)}
                >
                  {item.title}
                </Checkbox>
                <Image preview={false} src={item.placard} alt="" className="image"/>
              </Div>
            </div>
          ))}
     </div>
  </>
 );
};

export default PosterList;

在styled.ts中

import styled from 'styled-components';

const Div = styled.div<{
  clickId: boolean;
}>`
  padding-top: 8px;
  &:hover {
    border: 1px solid #4287ff;
    .ant-checkbox-wrapper .ant-checkbox-inner,
    .ant-checkbox .ant-checkbox-inner {
      border-color: #1965ff;
    }
  }
  width: 162px;
  height: 296px;
  /* margin-right: 16px; */
  margin-bottom: 16px;
  padding-left: 12px;
  background: #ffffff;
  border: ${(props: { clickId: boolean }) =>
    props.clickId ? '1px solid #1965FF' : '1px solid #ffffff'};
  border-radius: 2px;
  .title {
    display: inline-block;
    width: 112px;
    height: 22px;
    margin-left: 8px;
    overflow: hidden;
    color: #262626;
    font-weight: 400;
    font-size: 14px;
    line-height: 30px;
    white-space: nowrap;
    text-overflow: ellipsis;
  }
  .image {
    width: 138px;
    height: 244px;
    margin-top: 1px;
    /* margin: 1px 12px 12px; */
  }
`;
const AllCheckBox = styled(Checkbox)`
  margin-right: 4px;
  margin-bottom: 0;
`;
export { Div, AllCheckBox };

总结

  1. e.stopPropagation() 阻止事件冒泡,e.preventDefault() 阻止事件默认行为。
  2. filter() 过滤出一些符合条件的元素,返回一个新数组,不改变原数组。
  3. map() 数组的遍历,用来接收一个返回值,创建一个新数组,不改变原数组。
  4. forEach() 数组遍历,且只能够遍历数组,不接受返回值或返回值为 undefined。
  5. some() 检测数组中是否含有某一个值,返回一个布尔值,如果数组中有任意一个元素满足给定的条件,结果就为 true,否则为false。
  6. find() 查找数组中符合条件的元素,若有多个符合条件的元素,则返回第一个元素。
  7. findIndex() 查找数组中符合条件的元素索引,若有多个符合条件的元素,则返回第一个元素索引。
  8. splice() 没有参数,返回空数组,原数组不变。一个参数,从该参数表示的索引位开始截取,直至数组结束,返回截取的数组,原数组改变。两个参数,第一个参数表示开始截取的索引位,第二个参数表示截取的长度,返回截取的 数组,原数组改变;三个或者更多参数,第三个及以后的参数表示要从截取位插入的值。
  9. every() 方法用于检测数组所有元素是否都符合指定条件(通过函数提供),满足条件返回true,否则返回false。
可以通过以下步骤在 React 中实现在表格外添加一个复选框并实现全选或取消全选功能: 1. 在表格外部添加一个复选框,并为其编写状态处理函数。 ```jsx class Table extends React.Component { constructor(props) { super(props); this.state = { isChecked: false }; } handleCheckAll = e => { this.setState({ isChecked: e.target.checked }); }; render() { return ( <div> <input type="checkbox" checked={this.state.isChecked} onChange={this.handleCheckAll} /> <table> {/* ... */} </table> </div> ); } } ``` 2. 在表格行中添加一个复选框,并在其上绑定一个 `checked` 属性,该属性值应该与外部复选框的状态保持一致。 ```jsx class Table extends React.Component { constructor(props) { super(props); this.state = { isChecked: false, rows: [ { id: 1, name: 'Alice', isChecked: false }, { id: 2, name: 'Bob', isChecked: false }, { id: 3, name: 'Charlie', isChecked: false } ] }; } handleCheckAll = e => { const isChecked = e.target.checked; const rows = this.state.rows.map(row => ({ ...row, isChecked })); this.setState({ isChecked, rows }); }; handleCheckRow = (id, isChecked) => { const rows = this.state.rows.map(row => row.id === id ? { ...row, isChecked } : row); const isCheckedAll = rows.every(row => row.isChecked); this.setState({ isChecked: isCheckedAll, rows }); }; render() { return ( <div> <input type="checkbox" checked={this.state.isChecked} onChange={this.handleCheckAll} /> <table> <thead> <tr> <th>ID</th> <th>Name</th> <th>Is Checked</th> </tr> </thead> <tbody> {this.state.rows.map(row => ( <tr key={row.id}> <td>{row.id}</td> <td>{row.name}</td> <td> <input type="checkbox" checked={row.isChecked} onChange={e => this.handleCheckRow(row.id, e.target.checked)} /> </td> </tr> ))} </tbody> </table> </div> ); } } ``` 3. 当外部复选框的状态发生改变时,需要更新表格行的复选框状态;当表格行的复选框状态发生改变时,需要更新外部复选框的状态。 上述代码中已经实现了这些功能,因此整个应用已经可以正常工作了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值