Dylan Prem
Dylan Prem

Reputation: 174

React map function does not execute when the component is rendered

As you can see below in the dev tools screen shot, the child element does have props. My issue is I cannot get them to appear in the DOM when the component is first rendered. I have to click on the Link element again to re-render the component and only then does the map function work correctly (second screen shot). Another thing is I am using the same code in another component and it works fine. Help!

import React, { useState, useEffect } from 'react'
import firebase from 'firebase';
import NewsLetterListChildComponent from './children/NewsLetterListChildComponent';
import LoadingComponent from '../Loading/LoadingComponent';

function PublicNewsLetterListComponent({ user }) {
    const [ newsLetters, setNewsLetters ] = useState([]);
    const [ loading, setLoading ] = useState(false);
    const [ errors, setErrors ] = useState(false);

    useEffect(() => {
        let requestCancelled = false;

        const getNewsLetters = () => {
            setLoading(true);
            let newsLetterArray = [];
            firebase
                    .firestore()
                    .collection('newsLetters')
                    .get()
                    .then((querySnapshot) => {
                        querySnapshot.forEach((doc) => {
                        const listRef = firebase.storage().ref().child('newsLetterImagesRef/' + doc.id);
                        listRef
                            .getDownloadURL()
                            .then(url => {
                                newsLetterArray.push({ id: doc.id, data: doc.data(), image: url });
                            })
                            .catch(error => console.log(error))
                });
            });

            setNewsLetters(newsLetterArray);
            setLoading(false);
        };

        getNewsLetters();

         return () => {
            requestCancelled = true;
         };
    }, []);
    
    const renderContent = () => {
        if(loading) {
            return <LoadingComponent />
        } else {
            return <NewsLetterListChildComponent newsLetters={newsLetters} /> 
        }
    }
    return renderContent();

    
}

export default PublicNewsLetterListComponent
import React from 'react';
import { ListGroup, ListGroupItem, Row, Col } from 'reactstrap';

function NewsLetterListChildComponent({ newsLetters }) {
    return (
    <div>
        <Row>
            <Col md={{size: 6, offset: 3}}>
                <ListGroup>
                {newsLetters.map((item, index) => {
                    return (
                        <ListGroupItem key={index} className="m-1" ><h1>{item.data.title} </h1><img src={item.image} alt={item.data.title} className="thumb-size img-thumbnail float-right" /></ListGroupItem>
                    );
                })}
                </ListGroup>
            </Col>
        </Row>
    </div>
    )
}

export default NewsLetterListChildComponent;

Initial render and the list group is empty

Initial render and the list group is empty

after the re-render and now the list group is populated

after the re-render and now the list group is populated

Upvotes: 0

Views: 296

Answers (3)

lissettdm
lissettdm

Reputation: 13080

You need to call setNewsLetters when the data is resolved:

const getNewsLetters = async () => {
  setLoading(true);
  
  try {
    const newsLetters = await firebase
      .firestore()
      .collection("newsLetters")
      .get();
  
    const data = await Promise.all(
      newsLetters.docs.map(async (doc) => {
        const url = await firebase
          .storage()
          .ref()
          .child("newsLetterImagesRef/" + doc.id)
          .getDownloadURL();

        return {
          id: doc.id,
          data: doc.data(),
          image: url,
        };
      })
    );
    
    setNewsLetters(data);
  } catch (error) {
    console.log(error);
  } finally {
    setLoading(false);
  }
};

Upvotes: 2

Shubham Khatri
Shubham Khatri

Reputation: 281616

The useEffect code contains an async request and you are trying to update an array of newsLetters in state even before it will be fetched. Make use of Promise.all and update the data when it is available

useEffect(() => {
    let requestCancelled = false;

    const getNewsLetters = () => {
        setLoading(true);
        firebase
                .firestore()
                .collection('newsLetters')
                .get()
                .then((querySnapshot) => {
                    const promises = querySnapshot.map((doc) => {
                    const listRef = firebase.storage().ref().child('newsLetterImagesRef/' + doc.id);
                    return listRef
                        .getDownloadURL()
                        .then(url => {
                            return { id: doc.id, data: doc.data(), image: url };
                        })
                        .catch(error => console.log(error))
                    Promise.all(promises).then(newsLetterArray => { setNewsLetters(newsLetterArray);})
            });
        });

        
        setLoading(false);
    };

    getNewsLetters();

     return () => {
        requestCancelled = true;
     };
}, []);

Upvotes: 1

Cem Nisan
Cem Nisan

Reputation: 107

If you check newletters with if, your problem will most likely be resolved.

review for detail : https://www.debuggr.io/react-map-of-undefined/

if (newLetters){
  newLetters.map(item=> ...)
 }

Upvotes: 0

Related Questions