React Hooks useEffect для вызова обратного вызова проп из useCallback - PullRequest
0 голосов
/ 19 октября 2019

Я пытаюсь создать универсальный бесконечный скроллер с помощью React Hooks (и ResearchGate React Intersection Observer). Идея состоит в том, что родительский объект передаст сопоставленный массив данных JSX и обратный вызов, который асинхронно получит больше данных для этого массива, и когда наблюдатель пересечения сработает, потому что вы прокрутили достаточно вниз, чтобы показать значок загрузки, обратный вызов получитвызываемый и загружается больше данных.

Это работает достаточно хорошо, за исключением одного: esLint говорит мне, что, поскольку я вызываю функцию getMore (из реквизита) внутри useEffect, она должна зависеть от этого. эффект. Но поскольку в обратном вызове родителя я обращаюсь к длине его массива данных, этот массив должен быть зависимостью от useCallback. И затем этот обратный вызов изменяет массив.

TL; DR: я получаю условия гонки, которые вызывают асинхронный обратный вызов, если он не должен запускаться несколько раз, потому что ссылка на функцию обратного вызова изменяется и затем передаетсявплоть до того, что его вызывает.

Вот некоторый код для пояснения.

Обратный вызов в родительском:

const loadData = useCallback(async () => {
    if (hasMore) {
        const startAmount = posts.length;
        for (let i = 0; i < 20; ++i) {
            posts.push(`I am post number ${i + startAmount}.`);
            await delay(100);
        }
        setPosts([...posts]);
        setHasMore(posts.length < 100);
    }
}, [posts, hasMore]);

posts и hasMore - это просто переменные состояния, ссообщения передаются как массив данных в подпорки для ребенка. Эта функция передается дочернему элементу в props, который имеет это (getMore - это деструктурированная опора для обратного вызова, isLoading - просто логическая переменная состояния):

useEffect(() => {
    if (isLoading) {
        (async () => {
            await getMore();
            setIsLoading(false);
        })();
    }
}, [isLoading, getMore]);

Я устанавливаю isLoading в true длявызвать эффект;но это также срабатывает, потому что ссылка getMore изменяется, когда родитель загружает данные, а функция запоминает. Этого не должно быть. Я мог бы просто отключить esLint для этой строки, но я предполагаю, что есть лучшее решение, и я хотел бы знать, что это такое.

1 Ответ

0 голосов
/ 19 октября 2019

Решение: вообще не использовать useEffect. Просто вызовите функцию загрузки непосредственно из наблюдателя и установите этот набор isLoading и вызовите обратный вызов, вместо того, чтобы isLoading инициировал обратный вызов.

const loadData = async (observerEntry) => {
    if (observerEntry.isIntersecting && !disabled && !isLoading) {
        setIsLoading(true);
        await getMore();
        setIsLoading(false);
    }
};
...