1

So I did an interesting test because my data is not populating in my FlatList React Native element. I made two state variables: realList and fakeList:

state = { 
    realList: [],
    fakeList: [],
}

Then in componentWillMount, the following function is run which populates two arrays called real and fake. One with data pulled from Firebase, the other hardcoded with the SAME array information:

listenForMusic = () => {
        var dataRef = database.ref("music");
        let real = [];
        let fake = [];
        dataRef.orderByChild("date").on('child_added', (snap) => {
            var url = snap.val().youtubeURL;
            var vidTitle = snap.val().title;
            var thumb = snap.val().thumbnail;
            real.push({
                videoURL: url,
                title: vidTitle,
                thumbnail: thumb
            });
        });
        fake.push({videoURL: "https://youtu.be/AHukwv_VX9A", title: "MISSIO - Everybody Gets High (Audio)", thumbnail: "https://i.ytimg.com/vi/AHukwv_VX9A/hqdefault.jpg"}, {videoURL: "https://youtu.be/G-yWpz0xkWY", title: "SMNM - Million ft. Compulsive", thumbnail: "https://i.ytimg.com/vi/G-yWpz0xkWY/hqdefault.jpg"});
        this.setState({ 
            realList: real,
            fakeList: fake
        });
    }

Then I console.log both of the arrays after the render function:

render() {
        console.log("Actual", this.state.realList);
        console.log("Fake", this.state.fakeList);
        return (
            <View>
                <FlatList
                    data={this.state.fakeList}
                    renderItem={({item}) => <Text>{item.videoURL}</Text>}
                />
            </View>
        );
    }

and I see this:

enter image description here

And opening both:

enter image description here

So my question is, why does the "real" array look empty but still has data populated inside while the "fake" array displays that it holds two objects inside of it, even before we take a look inside??

In my FlatList, if I use my fakeList of data, I can display the data on screen, but if I use my realList, nothing shows up on my screen.

EDIT: Added full code for reference:

class VideoFeed extends React.Component {
    state = { 
        itemList: [],
    }

    componentDidMount() {
        this.listenForMusic(); 
    }

    listenForMusic = () => {
        var dataRef = database.ref("music");
        let items = [];
        dataRef.orderByChild("date").on('child_added', (snap) => {
            items.push({
                videoURL: snap.val().youtubeURL,
                title: snap.val().title,
                thumbnail: snap.val().thumbnail
            });
        });
        this.setState({ itemList: items })
    }

    _renderVideoItem = ({item}) => (
            <TouchableWithoutFeedback
                onPress={Actions.Submit}
            >
                <View style={styles.mediaContainer}>
                    <Image 
                        source={{uri: item.thumbnail }}
                        style={styles.mediaThumbnail}
                    />
                    <View style={styles.mediaMetaContainer}>
                        <View style={styles.topMetaContainer}>
                            <Text style={styles.mediaTitle}>
                                {item.title}
                            </Text>
                            <Text style={styles.sharedByUser}>
                            UNCVRD
                            </Text>
                        </View>
                        <View style={styles.bottomMetaContainer}>
                            <Icon
                                name='youtube-play'
                                type='material-community'
                                color='#ff0000'
                                size={16}
                            />
                            <View style={styles.bottomRightContainer}>
                                <Icon
                                    name='thumb-up'
                                    size={12}
                                    color='#aaa'
                                />
                                <Text style={styles.metaLikeCounter}>
                                    16
                                </Text>
                            </View>
                        </View>
                    </View>
                </View>
        </TouchableWithoutFeedback>
    );

    render() {
        console.log(this.state.itemList); // this list is populated
        return (
            <View>
                <FlatList
                    data={this.state.itemList}
                    renderItem={({item}) => {
                        console.log('item in render --> ', item); return (<Text>{item.videoURL}</Text>) }}
                />
            </View>
        );
    }
}
Jordan Lewallen
  • 1,681
  • 19
  • 54
  • 2
    The console window holds object references. The real is empty when the first console.log statement is executed. By the time you expand it. The events have fired. Your array is no longer empty. – Joe Apr 25 '18 at 18:41
  • However over the little blue `i` box shown in your second screenshot. Note that it is not shown in the first screenshot. – Felix Kling Apr 25 '18 at 18:45
  • @FelixKling I would not count this as an exact duplicate given the React context here. The Component lifecycle is also a factor which affects Jordan's issue. – dentemm Apr 25 '18 at 18:54
  • @dentemm thanks and I agree, the context is a little different, as I don't know why the object isn't re-rendering once the array is populated – Jordan Lewallen Apr 25 '18 at 18:55
  • Ah I think I see now. You are using the on() method from Firebase, right? The method componentDidMount (or componentWillMount) no longer exists when the Firebase callback is triggered, so you never receive the data. – dentemm Apr 25 '18 at 19:01
  • @dentemm oh interesting...I don't think I fully understand, but what is the best way to deal with that? – Jordan Lewallen Apr 25 '18 at 19:10
  • I edited my answer below, with a useful link which explains exactly what you want to do – dentemm Apr 25 '18 at 19:12

2 Answers2

2

The console window holds object references. The real is empty when the first console.log statement is executed. By the time you expand it. The events have fired. Your array is no longer empty. You can test this in the console:

let real = [];
let fake = [1, 2];
console.log(real, fake);
setTimeout(() => real.push(9))
Joe
  • 80,724
  • 18
  • 127
  • 145
1

When you inspect an object from a console log in more detail, your console will do a new lookup for that object. So what happens in your case is that at the point of your console log the array is empty, but gets populated shortly after. When you inspect it, you will see the object as it exists in memory at the time of inspection.

It is not advisable to do async stuff in componentWillMount (you will miss out on a re-render), better move that over to componentDidMount.

Edit: Since you want your component to automatically update when your Firebase data changes, the best way to go is to make use of a property in your React class which holds the reference to your Firebase data. More information on exactly how to do this you can find here. Check the section Realtime Database listener.

class VideoFeed extends React.Component {

    constructor(props) {
        super(props);
        this.state = {itemList: []};
        this.realList = firebaseApp.database().ref(); // create property
    }

    componentDidMount() {
        this.listenForMusic(this.realList); // pass property to listener method
    }

    listenForMusic = (list) => { // listener method
        let items = [];
        list.orderByChild("date").on('child_added', (snap) => {
            items.push({
                videoURL: snap.val().youtubeURL,
                title: snap.val().title,
                thumbnail: snap.val().thumbnail
            });
            this.setState({ itemList: items }); // update state
        });
    }

    // rest of component code ... 
}
dentemm
  • 6,231
  • 3
  • 31
  • 43
  • thanks, okay so that being said. How do I have my render function wait until the `listenForMusic` function is done loading information so that I can display the list of data properly? – Jordan Lewallen Apr 25 '18 at 18:46
  • @JordanLewallen: Why do you need to wait? At the beginning you have an empty list so you render an empty list. – Felix Kling Apr 25 '18 at 18:47
  • Just let it render a first time, you will get an automatic rerender as soon as the list is loaded and the state has been updated. You will probably not even notice the first render unless there is a slow internet connection – dentemm Apr 25 '18 at 18:48
  • @FelixKling because i need `realList` to populate with data before attempting to be used on `FlatList`. Because right now, nothing is displayed to screen since it uses realList as a data component in render before it has populated with data apparently – Jordan Lewallen Apr 25 '18 at 18:48
  • You could also show an ActivityIndicator for as long as the data has not been completely loaded from the api – dentemm Apr 25 '18 at 18:50
  • @dentemm okay i understand the logic because the data is updated in `setState` in my code, but the thing is, it is not re-rendering when the data is populated. My FlatList stays blank – Jordan Lewallen Apr 25 '18 at 18:52
  • @dentemm using setState works on my fakeList but doesn't work with my realList which has me puzzled – Jordan Lewallen Apr 25 '18 at 18:56
  • @dentemm this is what I was using before, which worked. However, it was recommended I use `FlatList` because `ListView` is depreciating – Jordan Lewallen Apr 25 '18 at 19:19
  • Yes that is true, but the technique of using a property is still correct. I will edit my answer a little to make it a little bit clearer – dentemm Apr 25 '18 at 19:21
  • @dentemm an edit would be super helpful thank you – Jordan Lewallen Apr 25 '18 at 19:29
  • @dentemm checking now :) – Jordan Lewallen Apr 25 '18 at 20:09
  • @dentemm you are the man, this works, only thing missing was defining the items array in listenForMusic :) – Jordan Lewallen Apr 25 '18 at 20:14
  • Glad it works, I fixed the missing variable definition to make the code snippet complete for future reference! Thanks! – dentemm Apr 26 '18 at 06:29