Почему состояние не обновляется должным образом в этом компоненте React Native? - PullRequest
0 голосов
/ 20 марта 2020

У меня есть компонент React Native, который используется на нескольких экранах, в которых я использую повторяющуюся функцию setTimeout для анимации карусели изображений. Карусель работает отлично, но я хочу правильно очистить таймер, когда экран отклонен от функции обратного вызова, возвращенной из ловушки useEffect. (Если я не очищаю таймер, то получаю неприятную ошибку, и я знаю, что все равно должен очистить таймеры.)

По какой-то причине, переменная состояния, которую я пытаюсь чтобы установить для setTimeout возвращенного идентификатора тайм-аута значение null в обратном вызове, возвращенном с useEffect.

Вот упрощенная версия моего кода:

const Carousel = () => {
    const [timeoutId, setTimeoutId] = useState(null);

    const startCarouselCycle = () => {
        const newTimeoutId = setTimeout(() => {
            // Code here that calls scrollToIndex for the FlatList.
        }, 5000);

        setTimeoutId(newTimeoutId);
    };

    const startNextCarouselCycle = () => {
        // Other code here.
        startCarouselCycle();
    };

    useEffect(() => {
        startCarouselCycle();

        return () => {
            // This is called when the screen with the carousel
            // is navigated away from, but timeoutId is null.
            // Why?!
            clearTimeout(timeoutId);
        };
    }, []);

    return (
        <FlatList
            // Non-essential code removed.
            horizontal={true}
            scrollEnabled={false}
            onMomentumScrollEnd={startNextCarouselCycle}
        />
    );
};

export default Carousel;

Кто-нибудь знает, почему состояние не будет корректно обновляться для использования в возвращенном обратном вызове useEffect? Спасибо.

Ответы [ 3 ]

0 голосов
/ 21 марта 2020

Судя по тому, что я видел, я не думаю, что нужно хранить идентификатор тайм-аута в состоянии. Попробуйте это:

import React, { useState, useEffect } from 'react';
import { FlatList } from 'react-native';

const Carousel = () => {
    let _timeoutId = null

    const startCarouselCycle = () => {
        const newTimeoutId = setTimeout(() => {
            // Code here that calls scrollToIndex for the FlatList.
        }, 5000);

        _timeoutId = newTimeoutId;
    };

    const startNextCarouselCycle = () => {
        // Other code here.
        startCarouselCycle();
    };

    useEffect(() => {
        startCarouselCycle();

        return () => {
            // This is called when the screen with the carousel
            // is navigated away from, but timeoutId is null.
            // Why?!
            clearTimeout(_timeoutId);
        };
    }, []);

    return (
        <FlatList
            // Non-essential code removed.
            horizontal={true}
            scrollEnabled={false}
            onMomentumScrollEnd={startNextCarouselCycle} />
    );
};

export default Carousel;
0 голосов
/ 24 марта 2020

Спасибо всем за ваши ответы и отзывы. Я пытался реализовать то, что все рекомендовали, но безрезультатно. К счастью, они отправили меня на правильный путь, и, насколько я знаю, возможно, в моем компоненте было что-то еще, что я не упомянул в своем вопросе, что делало вещи более сложными.

Все то же самое, после того, как несколько дней ударился головой о стену, я смог решить эту проблему следующим образом:

let timeoutId;

const Carousel = () => {
    const startCarouselCycle = () => {
        timeoutId = setTimeout(() => {
            // Code here that calls scrollToIndex for the FlatList.
        }, 5000);
    };

    const startNextCarouselCycle = () => {
        // Other code here.
        startCarouselCycle();
    };

    useEffect(() => {
        startCarouselCycle();

        return () => {
            if (timeoutId) {
                clearTimeout(timeoutId);
            }
        };
    }, []);

    return (
        <FlatList
            // Non-essential code removed.
            horizontal={true}
            scrollEnabled={false}
            onMomentumScrollEnd={startNextCarouselCycle}
        />
    );
};

export default Carousel;

Главное, что я изменил, - это перемещение переменной timeoutId в снаружи. функция рендеринга компонента. Функция рендеринга непрерывно вызывается, из-за чего timeoutId не обновлялся должным образом (понятия не имею, почему возникает проблема с закрытием?!).

Все равно перемещение переменной за пределы функции Carousel сделал трюк.

0 голосов
/ 20 марта 2020

Вы должны удалить массив зависимостей из вашего useEffect хука следующим образом:

useEffect(() => {
    startCarouselCycle();

    return () => {
        // This is called when the screen with the carousel
        // is navigated away from, but timeoutId is null.
        // Why?!
        clearTimeout(timeoutId);
    };
});

Это потому, что ваш эффект срабатывает один раз, когда компонент монтируется, и он получает только начальное значение вашего timeoutId.

...