|
|
|
@@ -1,202 +0,0 @@ |
|
|
|
/* |
|
|
|
* @Author: 赵伟 |
|
|
|
* @Date: 2024-04-15 10:01:29 |
|
|
|
* @Description: 自定义 hooks |
|
|
|
*/ |
|
|
|
import { FormInstance } from 'antd'; |
|
|
|
import { debounce } from 'lodash'; |
|
|
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; |
|
|
|
/** |
|
|
|
* 生成具有初始值的状态引用 |
|
|
|
* |
|
|
|
* @param initialValue - 状态的初始值 |
|
|
|
* @return 包含状态值、状态设置函数和可变引用对象的数组 |
|
|
|
*/ |
|
|
|
export function useStateRef<T>(initialValue: T) { |
|
|
|
const [value, setValue] = useState(initialValue); |
|
|
|
|
|
|
|
const ref = useRef(value); |
|
|
|
|
|
|
|
useEffect(() => { |
|
|
|
ref.current = value; |
|
|
|
}, [value]); |
|
|
|
|
|
|
|
return [value, setValue, ref] as const; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 生成一个自定义钩子,用于管理模态框的可见性状态。 |
|
|
|
* |
|
|
|
* @param initialValue - 模态框的初始可见性状态。 |
|
|
|
* @return 一个数组,包含可见性状态和打开和关闭模态框的函数。 |
|
|
|
*/ |
|
|
|
export function useVisible(initialValue: boolean) { |
|
|
|
const [visible, setVisible] = useState(initialValue); |
|
|
|
const ref = useRef(initialValue); |
|
|
|
|
|
|
|
const open = useCallback(() => { |
|
|
|
setVisible(true); |
|
|
|
}, []); |
|
|
|
|
|
|
|
const close = useCallback(() => { |
|
|
|
setVisible(false); |
|
|
|
}, []); |
|
|
|
|
|
|
|
useEffect(() => { |
|
|
|
ref.current = visible; |
|
|
|
}, [visible]); |
|
|
|
|
|
|
|
return [visible, open, close, ref] as const; |
|
|
|
} |
|
|
|
|
|
|
|
type Callback<T> = (state: T) => void; |
|
|
|
|
|
|
|
/** |
|
|
|
* 生成一个具有回调机制的可变状态值和更新它的函数。 |
|
|
|
* |
|
|
|
* @param initialValue - 初始状态值。 |
|
|
|
* @return 一个元组,包含当前状态值和用于更新状态的函数。 |
|
|
|
*/ |
|
|
|
export function useCallbackState<T>(initialValue: T) { |
|
|
|
const [state, _setState] = useState<T>(initialValue); |
|
|
|
const callbackQueue = useRef<Callback<T>[]>([]); |
|
|
|
useEffect(() => { |
|
|
|
callbackQueue.current.forEach((cb) => cb(state)); |
|
|
|
callbackQueue.current = []; |
|
|
|
}, [state]); |
|
|
|
const setState = (newValue: T | ((prevState: T) => T), callback?: Callback<T>) => { |
|
|
|
_setState(newValue); |
|
|
|
if (callback && typeof callback === 'function') { |
|
|
|
callbackQueue.current.push(callback); |
|
|
|
} |
|
|
|
}; |
|
|
|
return [state, setState] as const; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 用于追踪 DOM 元素尺寸的 hook。 |
|
|
|
* |
|
|
|
* @param initialWidth - 初始宽度。 |
|
|
|
* @param initialHeight - 初始高度。 |
|
|
|
* @param deps - 依赖列表。 |
|
|
|
* @return 一个元组,包含 DOM 元素的 ref、当前宽度和当前高度。 |
|
|
|
*/ |
|
|
|
export function useDomSize<T extends HTMLElement>( |
|
|
|
initialWidth: number, |
|
|
|
initialHeight: number, |
|
|
|
deps: React.DependencyList = [], |
|
|
|
) { |
|
|
|
const domRef = useRef<T>(null); |
|
|
|
const [width, setWidth] = useState(initialWidth); |
|
|
|
const [height, setHeight] = useState(initialHeight); |
|
|
|
|
|
|
|
useEffect(() => { |
|
|
|
const setDomHeight = () => { |
|
|
|
if (domRef.current) { |
|
|
|
setHeight(domRef.current.offsetHeight); |
|
|
|
setWidth(domRef.current.offsetWidth); |
|
|
|
} |
|
|
|
}; |
|
|
|
const debounceFunc = debounce(setDomHeight, 100); |
|
|
|
|
|
|
|
setDomHeight(); |
|
|
|
window.addEventListener('resize', debounceFunc); |
|
|
|
|
|
|
|
return () => { |
|
|
|
window.removeEventListener('resize', debounceFunc); |
|
|
|
}; |
|
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps |
|
|
|
}, [domRef, ...deps]); |
|
|
|
|
|
|
|
return [domRef, { width, height }] as const; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 用于在 modal 关闭时重置 Form 表单的 hook。 |
|
|
|
* |
|
|
|
* @param form - Ant Design Form 表单实例 |
|
|
|
* @param open - modal 是否打开 |
|
|
|
*/ |
|
|
|
export const useResetFormOnCloseModal = (form: FormInstance, open: boolean) => { |
|
|
|
const prevOpenRef = useRef<boolean>(); |
|
|
|
|
|
|
|
useEffect(() => { |
|
|
|
prevOpenRef.current = open; |
|
|
|
}, [open]); |
|
|
|
|
|
|
|
const prevOpen = prevOpenRef.current; |
|
|
|
|
|
|
|
useEffect(() => { |
|
|
|
if (!open && prevOpen) { |
|
|
|
form.resetFields(); |
|
|
|
} |
|
|
|
}, [form, prevOpen, open]); |
|
|
|
}; |
|
|
|
|
|
|
|
/** |
|
|
|
* Executes the effect function when the specified condition is true. |
|
|
|
* |
|
|
|
* @param effect - The effect function to execute. |
|
|
|
* @param when - The condition to trigger the effect. |
|
|
|
* @param deps - The dependencies for the effect. |
|
|
|
*/ |
|
|
|
export const useEffectWhen = (effect: () => void, when: boolean, deps: React.DependencyList) => { |
|
|
|
const requestFns = useRef<(() => void)[]>([]); |
|
|
|
useEffect(() => { |
|
|
|
if (when) { |
|
|
|
effect(); |
|
|
|
} else { |
|
|
|
requestFns.current.splice(0, 1, effect); |
|
|
|
} |
|
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps |
|
|
|
}, deps); |
|
|
|
|
|
|
|
useEffect(() => { |
|
|
|
if (when) { |
|
|
|
const fn = requestFns.current.pop(); |
|
|
|
fn?.(); |
|
|
|
} |
|
|
|
}, [when]); |
|
|
|
}; |
|
|
|
|
|
|
|
// 选择、全选操作 |
|
|
|
export const useCheck = <T>(list: T[]) => { |
|
|
|
const [selected, setSelected] = useState<T[]>([]); |
|
|
|
|
|
|
|
const checked = useMemo(() => { |
|
|
|
return selected.length === list.length && selected.length > 0; |
|
|
|
}, [selected, list]); |
|
|
|
|
|
|
|
const indeterminate = useMemo(() => { |
|
|
|
return selected.length > 0 && selected.length < list.length; |
|
|
|
}, [selected, list]); |
|
|
|
|
|
|
|
const checkAll = useCallback(() => { |
|
|
|
setSelected(checked ? [] : list); |
|
|
|
}, [list, checked]); |
|
|
|
|
|
|
|
const isSingleChecked = useCallback((item: T) => selected.includes(item), [selected]); |
|
|
|
|
|
|
|
const checkSingle = useCallback( |
|
|
|
(item: T) => { |
|
|
|
setSelected((prev) => { |
|
|
|
if (isSingleChecked(item)) { |
|
|
|
return prev.filter((i) => i !== item); |
|
|
|
} else { |
|
|
|
return [...prev, item]; |
|
|
|
} |
|
|
|
}); |
|
|
|
}, |
|
|
|
[isSingleChecked], |
|
|
|
); |
|
|
|
|
|
|
|
return [ |
|
|
|
selected, |
|
|
|
setSelected, |
|
|
|
checked, |
|
|
|
indeterminate, |
|
|
|
checkAll, |
|
|
|
isSingleChecked, |
|
|
|
checkSingle, |
|
|
|
] as const; |
|
|
|
}; |