Loadingspinner while Images is loading from URL

Viewed 517

In my React Native applikation I render a <FlatList> with Images. I pass the direct imageurl as source into the <Image> Component.

<FlatList 
           data={this.state.images}
           keyExtractor={item => item.imageToken}
           renderItem={({ item }) => (                         
                    <Image key={item.imageToken} style={{ marginRight: 2, marginTop: 2, width: '50%', opacity: 1 }} source={{ uri: item.imageUrl }} alt="Alternate Text" size="xl" />    )}    />

This means that the images are loaded in a different order because they are also different sizes. I would like to show a placeholder during loading.

The listAll() function resets isLoading to false before all images are displayed. Is there a 'trigger' when an image is fully visible in the view? I can't just build a single state for each image - I guess.

There will be many hundreds of pictures!

I think it's important to know that I extract the url from the google firestore images and store they as an array in state beforehand. See function getDownloadURL

Fullcode

import React, { Component } from 'react'
import { StyleSheet, SafeAreaView, ActivityIndicator } from 'react-native'
import { Image, FlatList, Center, Box } from "native-base"
import EventGalleryHeader from '../components/EventGalleryHeader.js'
import { getStorage, ref, getDownloadURL, list, listAll } from "firebase/storage"
import { LongPressGestureHandler, State } from 'react-native-gesture-handler'

export default class EventScreen extends Component {

    constructor(props) {
        super(props);
        this.storage = getStorage()
        this.pathToImages = '/eventimages/'
        this.eventImageSource = this.props.route.params.eventData.key
        this.imagesRef = this.pathToImages + this.eventImageSource
        this.state = {
            isLoading: true,
            images: [],
            event: {
                adress: this.props.route.params.eventData.adress,
                hosts: this.props.route.params.eventData.hosts,
                description: this.props.route.params.eventData.description,
                eventtitle: this.props.route.params.eventData.eventtitle,
                invitecode: this.props.route.params.eventData.invitecode,
                key: this.props.route.params.eventData.key,
                timestamp: this.props.route.params.eventData.timestamp,
            }
        }
    }

    componentDidMount() {
        this.getEventImageData()
    }

    componentWillUnmount() {
    }

    getEventImageData() {
        const images = []
        const event = {
            adress: this.props.route.params.eventData.adress,
            description: this.props.route.params.eventData.description,
            eventtitle: this.props.route.params.eventData.eventtitle,
            key: this.props.route.params.eventData.key,
            timestamp: this.props.route.params.eventData.timestamp,
        }

        listAll(ref(this.storage, this.imagesRef))
            .then((res) => {
                res.items.forEach((itemRef) => {   
                    getDownloadURL(itemRef)
                        .then((url) => {

                            const indexOfToken = url.indexOf("&token=")
                            const token = url.slice(indexOfToken + 7)
                            images.push({
                                "imageUrl": url,
                                "imageToken": token
                            });
                            this.setState({
                                images,
                                event,
                                isLoading: false,
                            });
                            // console.log(this.state.images)
                        })
                        .catch((error) => {
                            switch (error.code) {
                                case 'storage/object-not-found':
                                    break;
                                case 'storage/unauthorized':
                                    break;
                                case 'storage/canceled':
                                    break;
                                case 'storage/unknown':
                                    break;
                            }
                        });
                });

            }).catch((error) => {
            });
    }

    onLongPress = (event) => {
        if (event.nativeEvent.state === State.ACTIVE) {
            alert("I've been pressed for 800 milliseconds");
        }
    };

    render() {

        if (this.state.isLoading) {
            return (<Center style={styles.container} _dark={{ bg: "blueGray.900" }} _light={{ bg: "blueGray.50" }}>
                <ActivityIndicator size="large" color="#22d3ee" />
            </Center>
            )
        } else {
            return (
                <SafeAreaView style={styles.container} >
                    <FlatList _dark={{ bg: "blueGray.900" }} _light={{ bg: "blueGray.50" }}
                        style={styles.list}
                        numColumns={2}
                        ListHeaderComponent={<EventGalleryHeader data={this.state.event} />}
                        data={this.state.images}
                        keyExtractor={item => item.imageToken}
                        renderItem={({ item }) => (
                            <LongPressGestureHandler
                                onHandlerStateChange={this.onLongPress}
                                minDurationMs={800}
                            >
                                <Image key={item.imageToken} style={{ marginRight: 2, marginTop: 2, width: '50%', opacity: 1 }} source={{ uri: item.imageUrl }} alt="Alternate Text" size="xl" />
                            </LongPressGestureHandler>
                        )}
                    />
                </SafeAreaView>
            );
        }
    };

}
const styles = StyleSheet.create({
    container: {
        flex: 1,
    },
    image: {
        maxHeight: 450,
        width: '100%',
        height: 200,
        overflow: 'hidden',
    },
    list: {
        alignSelf: 'center',
    },
    gallery: {
        flex: 1,
        width: '100%',
        flexDirection: 'row',

    }
})
1 Answers

And again it shows how important it is to read the documentation properly beforehand and to look there first if you have any questions.

You can achieve the behavior I mentioned above with the following parameters.

loadingIndicatorSource link

Similarly to source, this property represents the resource used to render the loading indicator for the image, displayed until image is ready to be displayed, typically after when it got downloaded from network.

onLoad link

Invoked when load completes successfully.

onLoadEnd link

Invoked when load either succeeds or fails.

onLoadStart link

Invoked on load start.

Example: onLoadStart={() => this.setState({loading: true})}

Related