第一种方法,不使用任何插件和库
- import React, { useState, useEffect, SetStateAction, Dispatch } from 'react';
-
- // 定义expandedKeys的类型
- type ExpandedKeysType = Set<string>;
-
- // 自定义Hook:useExpandedKeys
- function useExpandedKeys(initialKeys: ExpandedKeysType): [ExpandedKeysType, (key: string) => void] {
- const [expandedKeys, setExpandedKeys] = useState(initialKeys);
-
- // 切换某个key的展开/收起状态
- const toggleByKey: (key: string) => void = (key) => {
- setExpandedKeys((prevKeys) => {
- const newKeys = new Set(prevKeys);
- if (newKeys.has(key)) {
- newKeys.delete(key);
- } else {
- newKeys.add(key);
- }
- return newKeys;
- });
- };
-
- // 使用useEffect来监听expandedKeys的变化
- useEffect(() => {
- // 这里可以执行依赖于expandedKeys的操作
- console.log('Expanded keys have changed:', expandedKeys);
- // 注意:这里的代码会在每次expandedKeys变化时执行
- }, [expandedKeys]); // 当expandedKeys变化时,这个effect会重新运行
-
- return [expandedKeys, toggleByKey];
- }
-
- // 定义Item的类型
- type ItemType = {
- id: string;
- name: string;
- };
-
- // 使用自定义Hook的的组件
- function MyComponent() {
- // 使用自定义Hook,初始值为一个空的Set
- const [expandedKeys, toggleByKey] = useExpandedKeys(new Set<string>());
-
- // 假设有一个item列表,我们为每个item渲染一个按钮来切换其展开/收起状态
- const items: ItemType[] = [
- { id: 'item1', name: 'Item 1' },
- { id: 'item2', name: 'Item 2' },
- // ...其他items
- ];
-
- return (
- <div>
- {items.map((item) => (
- <div key={item.id}>
- <button onClick={() => toggleByKey(item.id)}>
- {expandedKeys.has(item.id) ? '收起' : '展开'}
- </button>
- {expandedKeys.has(item.id) && <div>这里是{item.name}的展开内容</div>}
- </div>
- ))}
- </div>
- );
- }
-
- export default MyComponent;
第二种方法,和第一种大同小异,只不过写法高级一些,并使用了额外的库和插件
- import {useMap,useSet} from '@huse/collection';
- import constate from 'constate';
- import {Key,useCallback,useState} from 'react';
-
- export function useSetWithToggleAndAllChecked(){
- //存和当前全选状态相反的项的key
- // eg:已全展开时,存的是收起的项的key
- const [set,methods]=useSet<string>();
- const [allChecked,setAllChecked]=useState(false);
-
- const toggleByKey =useCallback(()=>{
- (key:string)=>{
- if(set.has(key)){
- methods.delete(key);
- }else {
- methods.add(key);
- }
- }
- },
- [set,methods]
- );
-
- const internalSetAllChecked=useCallback(()=>{
- (shouldAllChecked:boolean)=>{
- methods.clear();
- setAllChecked(shouldAllChecked);
- }
- },
- [set,methods,allChecked]
- );
-
- const checkIfExpandedByKey=useCallback(()=>{
- (key:string)=>{
- const has =set.has(key);
- if(allChecked){
- return !has;
- }else{
- return has;
- }
- }
- },
- [set,methods,allChecked]
- );
-
- return {
- set,
- methods:{toggleByKey,setAllChecked,internalSetAllChecked},
- isAllChecked:allChecked,
- checkoutExpandedByKey,
- }
- };
-
- export const [
- ExpandedRowProvider,
- useAreAllRowsExpanded,
- useToggleRowsExpanded,
- useToggleRowExpandedByKey,
- useCheckIfExpandedByKey,
- ]=constate(
- useSetWithToggleAndAllChecked,
- value=>value.isAllChecked,
- value=>value.methods.setAllChecked,
- value=>value.methods.toggleByKey,
- value=>value.checkIfExpandedByKey,
- )
ExpandedRowProvider相当于提供一个conText上下文
- //使用时,顶层组件
- <ExpandedRowProvider>
- </ExpandedRowProvider>
- //这个key就是后端返回的Table列表数据的每一项key值,或者是id
- const isExpanded = useCheckIfExpandedByKey()(key)
- const toggleByKey = useToggleRowExpandedByKey();
-
- <a onClick={()=>toggleByKey(key)}>{isExpanded?'收起':'展开'}</a>
代码一下简洁了好多.