useCallback 和 IntersectionObserver 的回调在获取新值之前仍然获取 Redux 状态的旧值

useCallback and IntersectionObserver's callback still gets the old value of a Redux state before getting the new value

在我的应用程序中,对于每个交叉点,获取 10 条时间戳比日期请求 less/earlier 的消息,然后将它们取消转移到 messages 数组中。日期请求的值是 messages 状态中第一项的时间戳。这是 IntersectionObserver 代码:

const MessagesContainer = forwardRef((props, ref) => {
    // ...

    const messages = useSelector(state => state.messages)
    const dispatch = useDispatch()

    const ioCallback = useCallback((entries, observer) => {
        if (entries[0].isIntersecting) {
            console.log(messages[0].timestamp)
            setLoading(true)

            axios.post('/api/messages', { date: messages[0].timestamp })
                .then(response => {
                    if (response.data.messages.length) {
                        dispatch({
                            type: 'ADD_TO_LIST',
                            name: 'messages',
                            payload: response.data.messages
                        })

                        ref.current.scrollTo(0, ref.current.scrollHeight / 3)
                    }
                    else {
                        observer.unobserve(target.current)
                    }

                    setLoading(false)
                })
        }
    }, [messages])


    useEffect(() => {
        const ioOptions = {
            root: ref.current,
            rootMargin: '0px',
            threshold: 1.0
        }

        const observer = new IntersectionObserver(ioCallback, ioOptions)

        if (target && target.current) {
            observer.observe(target.current)
        }
    }, [ioCallback])

    // ...
})

您会注意到 ioCallback() 中的 console.log(messages[0].timestamp)。对于每个交叉点,记录 messages 状态中第一项的时间戳。

第一个intersection/scroll没问题,它记录:

2020-06-24T20:02:41.214Z

但是下一个记录旧值和新值:

2020-06-24T20:02:41.214Z
2020-06-24T20:02:19.761Z

我需要知道如何防止 useCallback 获取旧状态值。

每次消息更改时,它都会重新创建 ioCallback,这会按预期触发 useEffect。它创建新的 IntersectionObserver,触发 ioCallback。但是老路口观察者呢?

您应该在 useEffect 挂钩中使用清除函数,它将在新的 useEffect 运行之前触发。它会停止旧的,然后你可以创建一个新的。

你可以通过返回一个函数来完成。所有引用都被保存,所以它就像将 observer.disconnect 包装在一个函数中并返回它一样简单:

useEffect(() => {
    const ioOptions = {
        root: ref.current,
        rootMargin: '0px',
        threshold: 1.0
    }

    const observer = new IntersectionObserver(ioCallback, ioOptions)

    if (target && target.current) {
        observer.observe(target.current)
    }

    return () => observer.disconnect();
}, [ioCallback])