0

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

0

3 Answers 3

2

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);
  }
};
Sign up to request clarification or add additional context in comments.

Comments

1

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;
     };
}, []);

4 Comments

Tried to plug that in but I'm getting Unhandled Rejection (TypeError): undefined is not iterable (cannot read property Symbol(Symbol.iterator)). I'll try to debug more
Copied it verbatim and I'm getting an unreachable code for Promise.all()
querySnapshot does not have the method map, use querySnapshot.docs.map(...) instead. The reason Promise.all() is unreachable becomes clear if you were to fix the indention (which should've been done in the question of OP).
@DylanPrem Never copy/paste code from answers into your own code. Try to look at what the answer tries to teach you, then write the code yourself. The code is meant for demonstration purposes. In this case it fails for at least two reasons, but the concept it tries to teach you is still correct.
0

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=> ...)
 }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.