如何(获取和)比较useEffect钩子内以前的状态值和新值



我把案例的简化版本放在上面。我正在尝试将新的网络结果与以前版本的结果进行比较。我将提取的数据存储为"通知"状态。如果没有变化,我不想setNotifications

在useEffect依赖数组中,esint大喊并希望我将notifications数组放入dep数组中。但我正在使用中更改notifications。如果我没有错的话,这将是一个无休止的循环。因为通知是一个对象数组,它们在每次渲染中都会发生变化(即使数据没有真正的变化(

你能解释一下在这种情况下使用useEffect时应该遵循哪个心理模型吗?

const NotificationBox = () => {
const [notifications, setNotifications] = useState([])
useEffect(() => {
client.get('notification?count=10').then((newResult) => {
if (!_.isEqual(notifications, newResult)) {
//notifications is stale
setNotifications(newResult)
}
})
}, [iHaveSomethingDifferentThatTriggersThisEffect]) 
//Also, ESLint: React Hook useEffect has a missing dependency: 'notifications'. Either include it or 
remove the dependency array.(react-hooks/exhaustive-deps)
}
export default NotificationBox

我的猜测是您的代码会很好地工作几乎一直

如果更新了notifications并且同时触发了fetch,那么您的钩子就有使用过时值运行的风险。

我认为你可以做得更好。例如,您可以制作一个自定义钩子来封装行为。

const notificationsCache = {
current: []
}
export function useNotifications() {
const [notifications, setNotifications] = useState(notificationsCache.current)
const update = useCallback(() => {
client.get('notification?count=10').then((newResult) => {
if (!_.isEqual(notificationsCache.current, newResult)) {
notificationsCache.current = newResult
setNotifications(notificationsCache.current)
}
})
}, [])
return [notifications, update]
}

这使用和exernal";ref";以缓存结果,因此所有使用钩子的组件都将共享一个缓存。如果不希望这样做,您只需将缓存移动到具有useRef的组件中,每个组件都将有一个独立的缓存。

export function useNotifications() {
const notificationsCache = useRef([])
const [notifications, setNotifications] = useState(notificationsCache.current)
const update = useCallback(() => {
client.get('notification?count=10').then((newResult) => {
if (!_.isEqual(notificationsCache.current, newResult)) {
notificationsCache.current = newResult
setNotifications(notificationsCache.current)
}
})
}, [])
return [notifications, update]
}

然后我们就可以

const [notifications, update] = useNotifications()

并且当我们想要触发重新获取时使用CCD_ 6。

这方面还有进一步改进的空间。如果您有多个组件使用挂钩并同时安装,该怎么办。如果您更新了该值,那么理想情况下,其他挂钩状态也应该得到更新。

这是使用新useId挂钩的最佳时机。

const notificationsCache = {
current: []
}
const listeners = {}
export function useNotifications() {
const id = useId()
const [notifications, setNotifications] = useState(notificationsCache.current)
useEffect(() => {
listeners[id] = setNotifications
return () => delete listeners[id]
}, [])
const setState = useCallback((newNotifications) => {
notificationsCache.current = newNotifications
for (const id in listeners) {
listeners[id](notificationsCache.current)
}
}, [])
const update = useCallback(() => {
client.get('notification?count=10').then((newResult) => {
if (!_.isEqual(notificationsCache.current, newResult)) {
setState(newResult)
}
})
}, [])
return [notifications, setState, update]
}

最新更新