推荐阅读
在实践中, 我们(我)经常会遇到, 当某变量变化的时候, 执行其他方法(副作用), 通常我们使用 useEffect
:
function useHook() {
const [varA] = useState();
useEffect(() => {
// do something
}, [varA]);
}
但是如果执行的方法中包含其他响应式变量 varB
, 我们需要 varB
的最新值, 但却不希望 varB
变化时触发 useEffect
重新执行, 那我们可能需要 hack:
function useHook() {
const [varA] = useState();
const [varB] = useState();
// mui 的 useEventCallback
const effect = useEventCallback(() => {
doSomething(varB);
});
useEffect(() => {
effect();
}, [varA, effect]);
}
因此我们可以写一个小小的语法糖来简化这一目的:
import { useEventCallback } from '@mui/material'
import useEnhancedEffect from '@mui/material/utils/useEnhancedEffect'
import { useRef } from 'react'
/**
* @example
* ``` tsx
* function Component() {
* const [count, setCount] = useState(0)
*
* // will be triggered when count changed
* useListen(count, (next, prev) => {
* console.log(prev, next)
* })
* }
* ```
*/
export function useListen<T>(
value: T,
callback: (next: T, prev: T | undefined) => void
) {
const isFirstCallbackRef = useRef(true)
const prevRef = useRef<T | undefined>(undefined)
const callbackRef = useEventCallback(callback)
useEnhancedEffect(() => {
// useEffect 在 dev 环境会执行 2 遍, 此处避免该行为造成的影响
if (value === prevRef.current && !isFirstCallbackRef.current) {
return
}
isFirstCallbackRef.current = false
callbackRef(value, prevRef.current)
prevRef.current = value
}, [value, callbackRef])
}
function Test() {
const [varA, setVarA] = useState()
useListen(varA, (next, prev) => {
console.log({ next, prev })
})
// ...
}
语义明晰
免除各种冗余的格式代码
useListen(varA, () => {
// 错误用法, 因为没有消除副作用 (removeEventListener)
window.addEventListener('event-name', callback)
})
如非特别声明,本站作品均为原创,遵循【自由转载-保持署名-非商用-非衍生 创意共享 3.0 许可证】。
对于转载作品,如需二次转载,请遵循原作许可。