Search code examples
reactjsreact-reduxspotifyredux-thunk

Wait for action creator to finish before calling another action creator - Redux thunk


i have 2 action creator in my app called createPlaylist and fillPlaylist, in order to call fillPlaylist i have to call createPlaylist first to get the id of the playlist (state.playlistDetail.id in fillPlaylist). the problem is when i called fillPlaylist, createPlaylist is still fetching the data so i can't call fillPlaylist because state.playlistDetail.id is undefined. i want to wait until createPlaylist finished the request then called fillPlaylist, how can i achieve this?

createPlaylist

export const createPlaylist = () => async (dispatch, getState) => {
    const state = getState()
    let uris = []

    const data =  JSON.stringify({
        'name': 'Playlist Name',
        'description': 'Playlist description',
    })

    const response = await spotify.post(`/users/${state.user.id}/playlists`, data, {
        'headers' : {
            'Authorization': 'Bearer ' + state.token,
            'Content-Type': 'application/json'
        },
        
    })

    dispatch({type: 'CREATE_PLAYLIST', payload: response.data})

}

fillPlaylist

export const fillPlaylist = (uris) => async (dispatch, getState) => {
    const state = getState()
    const data =  JSON.stringify({
        'uris': uris
    })
    console.log('state.playlistDetail', state)
    const response = await spotify.post(`/playlists/${state.playlistDetail.id}/tracks`, data, {
        'headers' : {
            'Authorization': 'Bearer ' + state.token,
            'Content-Type': 'application/json'
        },
        
    })

    dispatch({type: 'FILL_PLAYLIST', payload: response.data})
}

Result Component (this is where i called createPlaylist and fillPlaylist)

import React, {Component} from 'react';
import { connect } from 'react-redux';
import { createPlaylist, fillPlaylist } from '../actions'

class Result extends Component {
    constructor(props) {
        super(props);
        this.state = {}
    }

    createPlaylist = () => {
        this.props.createPlaylist()

        let uris = []

        this.props.recommendations.forEach(item => { 
            uris.push(item.uri)
        })

        this.props.fillPlaylist(uris)
    }

    render() {

        return (
                <div>
                    <h1>Result</h1>
                    <button onClick={this.createPlaylist}>Create Playlist</button>
                </div>
            );
        
    }
}

const mapStateToProps = state => {
    return {recommendations: state.recommendations};
};

export default connect(mapStateToProps, {createPlaylist, fillPlaylist})(Result);

Solution

  • you said you need to await for createPlaylist to finish fetching data, so why not merge the 2 functions into one, like this:

    export const createAndFillPlaylist = () => async (dispatch, getState) => {
      try {
        const state = getState();
        let uris = state.recommendations.map(item => item.uri);
        const PlayListBody = JSON.stringify({
          name: "Playlist Name",
          description: "Playlist description"
        });
        const headers = {
          Authorization: "Bearer " + state.token,
          "Content-Type": "application/json"
        };
        const { data } = await spotify.post(
          `/users/${state.user.id}/playlists`,
          PlayListBody,
          { headers }
        );
        dispatch({ type: "CREATE_PLAYLIST", payload: data });
        const createPlaylistBody = JSON.stringify({
          uris: uris
        });
        const response = await spotify.post(
          `/playlists/${data.id}/tracks`,
          createPlaylistBody,
          {
            headers
          }
        );
        dispatch({ type: "FILL_PLAYLIST", payload: response.data });
      } catch (error) {
        throw error;
      }
    };
    

    and for the component, no need to mapStateToProps. here's the code :

    import React, { Component } from "react";
    import { connect } from "react-redux";
    import { createAndFillPlayList } from "../actions";
    
    class Result extends Component {
      constructor(props) {
        super(props);
        this.state = {};
      }
    
      render() {
        return (
          <div>
            <h1>Result</h1>
            <button onClick={this.props.createAndFillPlayList}>
              Create Playlist
            </button>
          </div>
        );
      }
    }
    
    export default connect(
      null,
      { createAndFillPlayList }
    )(Result);