javascriptreactjsmethodspage-lifecycle

ComponentDidMount Uncaught Error in Promise


I have a function that I need to fire in a set order so I can receive the books from the logged in user. Right now this function works partially. It doesn't work when I refresh the page. It says there's an uncaught error in the promise. I need the getBooks function to finish before the console log fires. What's the way to write this?

async componentDidMount() {
     this.props.getBooks({
        user: this.props.auth.user.id
    
    })

    const wishlistbooks = this.props.auth.wishlistbooks;
    console.log(`WISHLIST:${wishlistbooks[0].book.title}`)
}

getBooks method

export const getBooks = (user) => dispatch => {

    axios
        .get("/api/users/wishlist", {
            params: {
                id: user
            }
        })
  
        .then(res => {
            const response = res.data;
            dispatch(setWishlist(response));
        })

        // .then(response => {
        //     console.log(response)

        //     dispatch(setWishlist({response}))
        // })

        .catch(() =>{
            console.log("Error!")
        })
    };
    
    export const setWishlist = response => {
        console.log(response)
        return {
            type: WISHLIST_LOADING,
            payload: response
        };
    };

Solution

  • Either a) separate the triggering of the asynchronous data from the usage of that data or b) lean into the asynchronous nature of it, and wait until it's over.

    In the first case, using the data (your console.log) would be handled in the render method. In that case, your render method would need to be able to handle renders that have no data available (e.g. render a loading... message or something.)

      render() {
        if (!this.props.auth.wishlistbooks) {
          return 'Loading...'
        }
        
        const wishlistbooks = this.props.auth.wishlistbooks;
        return <>WISHLIST: {wishlistbooks[0].book.title}
      }
    

    In the second case, await getBooks and make sure you return the axios call from it.

    async componentDidMount() {
      await this.props.getBooks({ // this causes componentDidMount to wait for getBooks to complete
        user: this.props.auth.user.id
      })
    
      const wishlistbooks = this.props.auth.wishlistbooks;
      console.log(`WISHLIST:${wishlistbooks[0].book.title}`)
    }
    
    export const getBooks = (user) => dispatch => {
      return axios.get("/api/users/wishlist", { // returning the axios.get passes the promise back up to componentDidMount
        // ...
    
    

    Why would you choose one or the other? The first case is more useful for rendering. If the data you're fetching is something you're relying on to display, you'll need to handle the times when you don't have it. The second case is more useful for data you're using internally only. (Perhaps it's logging or metrics or you're just testing how something works.)