Blueboye
Blueboye

Reputation: 1494

How to call a function from action before dispatching to reducer?

I am working on a React Redux app. I have an action in which I am calling a utility function before dispatching the action.

import GetDataFromAPIs from "../utils/menu-creator";
export const UPDATE_DATA = 'UPDATE_DATA';

export const updateData = () => {
    const newData = GetDataFromAPIs();

    return{
        type: UPDATE_DATA, 
        payload: newData
    };
};

And, here is what GetDataFromAPIs.js is loosely doing:

import React from "react";
import axios from "axios";

const apiOneData = axios.get('/api/a');
const apiTwoData = axios.get('/api/b');


const GetDataFromAPIs = () => {
    axios.all([apiOneData, apiTwoData]).then(axios.spread((...responses) => {
        var firstDataSet = responses[0].data
        var secondDataSet = responses[1].data;
        var dataObject = [];

        dataObject.push(firstDataSet);
        dataObject.push(secondDataSet);

        return dataObject;

    })).catch(errors => {
        console.log("There was an error.");
    })
}

export default GetDataFromAPIs;

But when the application runs newData is set as undefined. I am trying to step through GetDataFromAPIs.js but it traces till axios.all and then breaks out of the function. I don't see any error in the console.

So what I am guessing is that action is dispatched before the two axios.all is resolved. I thought this would be synchronous (newData will get the data back and then the action will be dispatched). Am I doing this wrong?

Upvotes: 2

Views: 1310

Answers (3)

Barry Michael Doyle
Barry Michael Doyle

Reputation: 10618

You need to explicitly tell the action to wait to get that data asynchronously. To do that, make the following changes:


Your GetDataFromAPIs function should look this this:

const GetDataFromAPIs = async () => {
    // everything in here stays the same
}

And your updateData function should look like this:

export const updateData = async (dispatch) => {
    const newData = await GetDataFromAPIs();

    return dispatch({
        type: UPDATE_DATA, 
        payload: newData
    });
};

Be sure to dispatch your action.

Note:

If you haven't got redux-thunk setup then you'll need to install that npm module and update your createStore to look something like this:

// other imports
import { applyMiddleware, createStore } from 'redux'
import thunk from 'redux-thunk'

const store = createStore(rootReducer, applyMiddleWare(thunk))

Upvotes: 2

O. Beaulieu
O. Beaulieu

Reputation: 63

GetDataFromApis doesn't return.

You should do

const GetDataFromApis = async () => {
    return axios.all(...

GetDataFromApis will also return a Promise so you should await it in updateData like so:

export const updateData = async () => {
    const newData = await GetDataFromAPIs();

    return {
        type: UPDATE_DATA, 
        payload: newData
    };
};

Upvotes: 0

larz
larz

Reputation: 5766

I think async/await is what you need.

export const updateData = async () => {
    const newData = await GetDataFromAPIs();

    return{
        type: UPDATE_DATA, 
        payload: newData
    };
};


const GetDataFromAPIs = async () => {
  try {
    const responses = await axios.all([apiOneData, apiTwoData]).then(axios.spread((...responses);
    var firstDataSet = responses[0].data
    var secondDataSet = responses[1].data;
    var dataObject = [];

    dataObject.push(firstDataSet);
    dataObject.push(secondDataSet);

    return dataObject;

  } catch(errors) {
      console.log("There was an error.");
  })
}

Upvotes: 0

Related Questions