Как очистить запрос Image.getSize при размонтировании компонента React Native? - PullRequest
1 голос
/ 24 марта 2020

У меня есть компонент React Native, который делает запрос Image.getSize для каждого изображения в компоненте. Затем в рамках обратного вызова запросов Image.getSize я установил некоторое состояние для своего компонента. Это все работает нормально, но проблема в том, что пользователь может перейти от экрана, на котором используется компонент, до того, как ответит один или несколько Image.getSize запросов, что затем вызывает ошибку «утечка памяти без операции» вверх, потому что я пытаюсь изменить состояние после того, как компонент был размонтирован.

Итак, мой вопрос: как я могу остановить запрос Image.getSize на попытку изменить состояние после размонтирования компонента? Вот упрощенная версия моего кода компонента. Спасибо.

const imgWidth = 300; // Not actually static in the component, but doesn't matter.

const SomeComponent = (props) => {
    const [arr, setArr] = useState(props.someData);

    const setImgDimens = (arr) => {
        arr.forEach((arrItem, i) => {
            if (arrItem.imgPath) {
                const uri = `/path/to/${arrItem.imgPath}`;

                Image.getSize(uri, (width, height) => {
                    setArr((currArr) => {
                        const newWidth = imgWidth;
                        const ratio = newWidth / width;
                        const newHeight = ratio * height;

                        currArr = currArr.map((arrItem, idx) => {
                            if (idx === i) {
                                arrItem.width = newWidth;
                                arrItem.height = newHeight;
                            }

                            return arrItem;
                        });

                        return currArr;
                    });
                });
            }
        });
    };

    useEffect(() => {
        setImgDimens(arr);

        return () => {
            // Do I need to do something here?!
        };
    }, []);

    return (
        <FlatList
            data={arr}
            keyExtractor={(arrItem) => arrItem.id.toString()}
            renderItem={({ item }) => {
                return (
                    <View>
                        { item.imgPath ?
                            <Image
                                source={{ uri: `/path/to/${arrItem.imgPath}` }}
                            />
                            :
                            null
                        }
                    </View>
                );
            }}
        />
    );
};

export default SomeComponent;

1 Ответ

1 голос
/ 24 марта 2020

Мне пришлось реализовать нечто подобное, я начинаю с инициализации переменной с именем isMounted.

Устанавливается на true, когда компонент монтируется, и false, когда компонент монтируется.

Перед вызовом setImgDimens необходимо проверить, смонтирован ли компонент. Если нет, она не будет вызывать функцию и, следовательно, не будет обновлять состояние.

const SomeComponent = (props) => {
  const isMounted = React.createRef(null);
  useEffect(() => {
    // Component has mounted
    isMounted.current = true;

    if(isMounted.current) {
      setImgDimens(arr);
    }

    return () => {
      // Component will unmount
      isMounted.current = false;
    }
  }, []);
}

Редактировать : это ответ, который работал для меня, но для чего он Стоит, мне пришлось переместить переменную isMounted в вне функции SomeComponent, чтобы она работала Кроме того, вы можете просто использовать обычную переменную вместо createRef для создания ссылки и т. Д. c.

В принципе, для меня сработало следующее:

let isMounted;

const SomeComponent = (props) => {
    const setImgDimens = (arr) => {
        arr.forEach((arrItem, i) => {
            if (arrItem.imgPath) {
                const uri = `/path/to/${arrItem.imgPath}`;

                Image.getSize(uri, (width, height) => {
                    if (isMounted) { // Added this check.
                        setArr((currArr) => {
                            const newWidth = imgWidth;
                            const ratio = newWidth / width;
                            const newHeight = ratio * height;

                            currArr = currArr.map((arrItem, idx) => {
                                if (idx === i) {
                                    arrItem.width = newWidth;
                                    arrItem.height = newHeight;
                                }

                                return arrItem;
                            });

                            return currArr;
                        });
                    }
                });
            }
        });
    };

    useEffect(() => {
        isMounted = true;
        setImgDimens(arr);

        return () => {
            isMounted = false;
        }
    }, []);
};
...