0

In my app, for every intersection, fetch 10 messages with timestamps that are less/earlier than the date request, then unshift them into messages array. The value of the date request is the timestamp of the first item in messages state. Here's the IntersectionObserver code:

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])

    // ...
})

You would notice the console.log(messages[0].timestamp) in ioCallback(). For every intersection, log the timestamp of the first item in messages state.

The first intersection/scroll is okay, it logs:

2020-06-24T20:02:41.214Z

But the next one logs the old value and the new value:

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

I need to know how to prevent useCallback from getting the old state value.

jstarnate
  • 319
  • 3
  • 15

1 Answers1

3

Every time the message changes, it recreates the ioCallback, which triggers the useEffect as expected. It creates new IntersectionObserver, which triggers ioCallback. But what about the old intersection observer?

You should use cleanup function in the useEffect hook, that will be triggered before the new useEffect runs. It will stop the old one, and then you can create a new one.

You can do it by returning a function. All references are saved, so it as simple as wrapping observer.disconnect in a function and returning it:

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])
Vlad Gincher
  • 1,052
  • 11
  • 20