Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React/Redux State is empty on second action call

I'm still relatively new to React/Redux so apologies if this is a simple question but I am yet to find a solution.

I have an two actions:

// DESTINATIONS
// ==============================================

export const DESTINATION_REQUEST = 'DESTINATION_REQUEST';
export const DESTINATION_SUCCESS = 'DESTINATION_SUCCESS';
export const DESTINATION_FAILURE = 'DESTINATION_FAILURE';

export function loadDestination (params, query) {

    const state = params.state ? `/${params.state}` : '';
    const region = params.region ? `/${params.region}` : '';
    const area = params.area ? `/${params.area}` : '';

    return (dispatch) => {
        return api('location', {url: `/accommodation${state}${region}${area}`}).then((response) => {
            const destination = formatDestinationData(response);

            dispatch({
                type: DESTINATION_SUCCESS,
                destination
            });
        });
    };
}





// PROPERTIES
// ==============================================

export const PROPERTIES_REQUEST = 'PROPERTIES_REQUEST';
export const PROPERTIES_SUCCESS = 'PROPERTIES_SUCCESS';
export const PROPERTIES_FAILURE = 'PROPERTIES_FAILURE';

export function loadProperties (params, query, rows = 24) {

    return (dispatch, getState) => {

        console.log(getState());

        return api('search', {locationId: xxxxxx, rows: rows}).then((response) => {
            const properties = response.results.map(formatPropertiesData);

            dispatch({
                type: PROPERTIES_SUCCESS,
                properties
            });
        });
    };
}

these are combined with their relative reducers:

// DESTINATIONS REDUCERS
// ==============================================

export default function destination (state = [], action) {
    switch (action.type) {
    case DESTINATION_SUCCESS:
        return action.destination;
    default:
        return state;
    }
}





// PROPERTIES REDUCERS
// ==============================================

export default function properties (state = [], action) {
    switch (action.type) {
    case PROPERTIES_SUCCESS:
        return action.properties;
    default:
        return state;
    }
}

and they are called from within a component (connectDataFetchers loops through the called actions and returns them to the component for server side rendering):

// PROPTYPES
// ==============================================

Search.propTypes = {
    destination: PropTypes.object.isRequired,
    properties: PropTypes.array.isRequired
};





// ACTIONS
// ==============================================

function mapStateToProps ({destination, properties}) {
    return {destination, properties};
}





// CONNECT & EXPORT
// ==============================================

export default connect(mapStateToProps)(
    connectDataFetchers(Search, [loadDestination, loadProperties])
);

export default function connectDataFetchers (Component, actionCreators) {
    return class DataFetchersWrapper extends React.Component {
        static propTypes = {
            dispatch: React.PropTypes.func.isRequired,
            location: React.PropTypes.object.isRequired,
            params: React.PropTypes.object.isRequired
        };
        
        static fetchData (dispatch, params = {}, query = {}) {
            return Promise.all(
                actionCreators.map((actionCreator) => dispatch(actionCreator(params, query)))
            );
        }

        componentDidMount () {
            DataFetchersWrapper.fetchData(
                this.props.dispatch,
                this.props.params,
                this.props.location.query
            );
        }

        render () {
            return (
                <Component {...this.props} />
            );
        }
    };
}

I need to run the first action (loadDestination) which will return an ID then needs to then pass to the second action to load properties with that location ID.

If I hardcode a locationID this works fine, but if I try to access the state in loadProperties via getState() then it returns { destination: [], properties: [] }.

Is there a way to access the value from the first action via the state?

SOLUTION

Managed to get this to work following the suggestion from @pierrepinard_2

I created a new action that dispatches the two other actions in the order I needed:

// SEARCH
// ==============================================

export function loadSearch (params, query) {
    
    return (dispatch) => {
        return dispatch(
            loadDestination(params, query)
        ).then(() => {
            return dispatch(
                loadProperties(params, query)
            )
        })
    }
}

// DESTINATIONS
// ==============================================

export const DESTINATION_REQUEST = 'DESTINATION_REQUEST';
export const DESTINATION_SUCCESS = 'DESTINATION_SUCCESS';
export const DESTINATION_FAILURE = 'DESTINATION_FAILURE';

export function loadDestination (params, query) {

    const state = params.state ? `/${params.state}` : '';
    const region = params.region ? `/${params.region}` : '';
    const area = params.area ? `/${params.area}` : '';

    return (dispatch) => {
        return api('location', {url: `/accommodation${state}${region}${area}`}).then((response) => {
            const destination = formatDestinationData(response);
            
            dispatch({
                type: DESTINATION_SUCCESS,
                destination
            });
        });
    };
}
  
  // PROPERTIES
// ==============================================

export const PROPERTIES_REQUEST = 'PROPERTIES_REQUEST';
export const PROPERTIES_SUCCESS = 'PROPERTIES_SUCCESS';
export const PROPERTIES_FAILURE = 'PROPERTIES_FAILURE';

export function loadProperties (params, query, rows = 24) {

    return (dispatch, getState) => {
        return api('search', {locationId: getState().destination.id, rows: rows}).then((response) => {
            const properties = response.results.map(formatPropertiesData);

            dispatch({
                type: PROPERTIES_SUCCESS,
                properties
            });
        });
    };
}

then in the component I just request the one action:

export default connect(mapStateToProps)(
    connectDataFetchers(Search, [loadSearch])
);
like image 556
Paul Avatar asked Oct 18 '22 11:10

Paul


1 Answers

You use Promise.all() in your fetchData() method: your actions are dispatched in parallel, not one after another.

To ensure that you call first destination then properties, you should create one specific async action creator for your Search component. This async action creator would implement the successive requests you need in this case.

like image 179
pierrepinard_2 Avatar answered Oct 21 '22 06:10

pierrepinard_2