随便记录一下在打工中写的一些简单的hook
1.setTimeout写轮询
注意判断卸载情况
import { useEffect, useRef } from 'react';
const useLoopRequest = (
request: (...args: any) => Promise<any>,
interval: number,
immediate: boolean = false,
) => {
const timerRef = useRef<any>(null);
const requestRef = useRef<() => Promise<any>>(request);
const intervalRef = useRef<number>(interval);
const mountedRef = useRef(true);
const requestFn = async (intervalTime: number = intervalRef.current) => {
if (timerRef.current) clearTimeout(timerRef.current);
//防止在组件销毁的时候,定时器任务还在运行,即使销毁定时器,因为异步任务后就是requestFn,这个函数还是会重新开启定时器
if (!mountedRef.current) return; //卸载后就停止
timerRef.current = setTimeout(async () => {
await requestRef.current();
requestFn();
}, intervalTime);
};
const switchTabFunc = () => {
if (document.visibilityState === 'hidden' && timerRef.current) {
clearTimeout(timerRef.current);
} else if (document.visibilityState === 'visible') {
requestFn(0);
}
};
useEffect(() => {
//切换同pathName的时候,不会销毁组件触发该回调
return () => {
mountedRef.current = false;
if (timerRef.current) {
clearTimeout(timerRef.current);
}
document.removeEventListener('visibilitychange', switchTabFunc);
};
}, []);
return {
startLoop: () => {
document.removeEventListener('visibilitychange', switchTabFunc);
document.addEventListener('visibilitychange', switchTabFunc);
requestFn(immediate ? 0 : intervalRef.current);
},
};
//考虑切屏的情况
};
export default useLoopRequest;
2. 使用antd pro-table的受控列状态hook
这是把所有表格受控列存在一个localStorage/sessionStorage的一个key里,也可以修改一下不放在同个key
业务场景:tab组件,每个tab一个表格,每个表格列都受控,需要浏览器缓存,并且几个表格间会有相同列
import { useEffect, useState } from 'react';
const useColumnState = (
key: string,
defaultValue: any,
persistenceType: 'localStorage' | 'sessionStorage',
) => {
if(!['localStorage', 'sessionStorage'].includes(persistenceType)) {
throw new Error('Invalid persistenceType');
}
const keyPrefix = 'sys_tableColumnsState';
const [columnsStateMap, setColumnsStateMap] = useState<any>(defaultValue);
useEffect(() => {
const obj = JSON.parse(window[persistenceType].getItem(keyPrefix) || '{}');
const hasAnyProperty = (objj: any) => {
//这里没有用Object.keys()去判断是否是空对象,是因为听过Object.keys()性能一般,
//就用for in取代了,但感觉for in遍历原型链也不见得比Object.keys()好到哪里:)
for (const keyy in objj) {
if (objj.hasOwnProperty(keyy)) {
return true;
}
}
return false;
};
if (obj[key] && hasAnyProperty(obj[key])) {
setColumnsStateMap(obj[key]);
} else {
obj[key] = columnsStateMap;
window[persistenceType].setItem(keyPrefix, JSON.stringify(obj));
}
}, []);
useEffect(() => {
const obj = JSON.parse(window[persistenceType].getItem(keyPrefix) || '{}');
obj[key] = columnsStateMap;
window[persistenceType].setItem(keyPrefix, JSON.stringify(obj));
}, [columnsStateMap]);
return {
value: columnsStateMap,
onChange: setColumnsStateMap,
persistenceType: persistenceType,
};
};
export default useColumnState;
3. 异步toast
import { message } from 'antd';
const useLoadingToast = () => {
const toast = (content: React.ReactNode) => {
return message.loading(content, 0);
};
return {
toast,
};
};
export default useLoadingToast;
4. select按照options顺序排序不按照选择顺序
数据量小不分页的options可以这样做,其他还是算了,都是为了应付产品奇葩需求不考虑任何一点性能
const useSortOptions = (options: any[], handleChange: (...r: any) => void) => {
// console.log('useSortOptions--', options);
const onChange = (...args: any) => {
// console.log(args[0]);
if (Array.isArray(args[0])) {
args[0] = options.reduce((acc: any[], item: any) => {
if (args[0].includes(item.value)) {
acc.push(item.value);
}
return acc;
}, []);
}
// console.log('sort--', args[0]);
handleChange(...args);
};
return { onChange };
};
export default useSortOptions;
5. 使用antd select发现的一个问题
- 远程搜索
- 多选
- 初始有选中项
满足这三个情形的时候就会出现这样的问题,再次搜索选中触发的onChange函数的option参数会丢失初始选中项的值,变成空对象{}。
import React, { useEffect, useRef } from 'react';
import { Select } from 'antd';
const SearchSelect = (props: any) => {
const initSelectedOption = useRef([]);
const first = useRef(true)
const onChange = (value: any, option: any[] | any) => {
if (props.mode === 'multiple' && props.onSearch && first.current) {
const selectedOption = initSelectedOption.current.filter((item: any) => value.includes(item.value))//历史选中的option
props.onChange(value, selectedOption.concat(option).filter((item: any) => !!item.value))
first.current = false
return;
}
if (props.onChange) {
props.onChange(value, option)
}
}
useEffect(() => {
initSelectedOption.current = props.value || [];
}, [])
return (
<Select
{...props}
onChange={onChange}
/>
)
}
export default SearchSelect;