Async action not running Async React Redux Thunk - javascript

I am trying to build a React App that fetches data from 7 api's, on initial load, then stores all the data in Redux, to use within the app. I am pretty new to React and a bit of a coding noob.
I have a dispatch function on my first app page that fires on componentDidMount() and runs the fetchData() function in the action below.
export function getDataPending(actionType) {
return {
type: "FETCH_" + actionType.toUpperCase() + "_PENDING"
};
}
export function getDataSuccess(actionType, data) {
return {
type: "FETCH_" + actionType.toUpperCase() + "_FULFILLED",
payload: data
};
}
export function getDataFailure(actionType, data) {
return {
type: "FETCH_" + actionType.toUpperCase() + "_REJECTED",
payload: data
};
}
//Sub data fetches
export function fetchNicSalts() {
return dispatch => {
dispatch(getDataPending("nic_salt"));
axios
.get(
"API_FEED"
)
.then(response => {
dispatch(getDataSuccess("nic_salt", response));
})
.catch(err => {
dispatch(getDataFailure("nic_salt", err));
});
};
}
export function fetchFreebase() {
return dispatch => {
dispatch(getDataPending("freebase"));
axios
.get(
"API_FEED"
)
.then(response => {
dispatch(getDataSuccess("freebase", response));
})
.catch(err => {
dispatch(getDataFailure("freebase", err));
});
};
}
//Main data fetch
export function fetchData() {
return dispatch => {
dispatch(getDataPending("data"));
dispatch(fetchFreebase());
dispatch(fetchNicSalts());
dispatch(getDataSuccess("data", null));
};
}
There are only two api fetches at the moment, as I am trying to get them to run async.
Ideally what I want to happen the dispatches to run in this order.
FETCH_DATA_PENDING
FETCH_FREEBASE_PENDING
FETCH_FREEBASE_FULFILLED
FETCH_NIC_SALT_PENDING
FETCH_NIC_SALT_FULFILLED
**** next api *****
FETCH_DATA_FULFILLED
The data is being stored at the moment just in the wrong order. Please see image for current order.
My data fulfillment order
As I say, I am self taught and not sure if I am on the right path, any advice is welcome. TIA.

You need to make your fetchFreebase and fetchNicSalts return the axios calls so that you can await them in the fetchData action.
This would look like this
export function getDataPending(actionType) {
return {
type: "FETCH_" + actionType.toUpperCase() + "_PENDING"
};
}
export function getDataSuccess(actionType, data) {
return {
type: "FETCH_" + actionType.toUpperCase() + "_FULFILLED",
payload: data
};
}
export function getDataFailure(actionType, data) {
return {
type: "FETCH_" + actionType.toUpperCase() + "_REJECTED",
payload: data
};
}
//Sub data fetches
export function fetchNicSalts() {
return dispatch => {
dispatch(getDataPending("nic_salt"));
return axios
.get(
"API_FEED"
)
.then(response => {
dispatch(getDataSuccess("nic_salt", response));
})
.catch(err => {
dispatch(getDataFailure("nic_salt", err));
});
};
}
export function fetchFreebase() {
return dispatch => {
dispatch(getDataPending("freebase"));
return axios
.get(
"API_FEED"
)
.then(response => {
dispatch(getDataSuccess("freebase", response));
})
.catch(err => {
dispatch(getDataFailure("freebase", err));
});
};
}
//Main data fetch
export function fetchData() {
return async (dispatch) => {
dispatch(getDataPending("data"));
await dispatch(fetchFreebase());
await dispatch(fetchNicSalts());
dispatch(getDataSuccess("data", null));
};
}

Related

How to wait for action to complete before accessing the Vue Store state?

I have Vuejs/Nuxtjs application within which I need to access a Vuex store state after it has been modified by Vuex action. Currently when I try to run the action and assignment then I get the old state and not the one which was updated after action.
How to make the code wait for action completion then run the next statement? Following is the code I have currently:
Vuejs Component:
<template>
<div>
<input v-model="formData.value" type="text">
<button #click="performAction">
Click Me
</button>
</div>
</template>
<script>
export default {
data () {
return {
formData: {
value: '',
returnValue: ''
}
}
},
methods: {
performAction () {
// Set the value within the Vuex Store
this.$store.commit('modules/DataStore/populateData', this.formData.value)
// Perform the Action
this.$store.dispatch('modules/DataStore/getData').then(() => {
console.log("AFTER COMPLETE ACTION")
})
// Assign the update value to the variable
this.formData.returnValue = this.$store.state.modules.DataStore.data
}
}
}
</script>
<style>
</style>
Vuex Store:
export const state = () => ({
data:''
})
export const mutations = {
populateData (state, data) {
state.data = data
}
}
export const actions = {
getData ({ commit, state, dispatch }) {
const headers = { 'Content-Type': 'application/json' }
this.$axios
.post('/getUrlData', state.data, { headers })
.then((response) => {
console.log("WITHIN RESPONSE")
commit('populateData',response.data)
})
.catch((error) => {
commit('populateData', 'Unable to obtain data, Error : ' + error)
})
}
}
Following are the thing I tried and nothing is working at the moment:
I tried the .then() function.
I tried Async and await but both are not working
Any suggestions will be really appreciated. Thanks in advance.
You can create getter in vuex :
export const getters = {
getData: (state) => state.data,
};
export const actions = {
async setData ({ commit }, data) {
const headers = { 'Content-Type': 'application/json' }
await this.$axios
.post('/getUrlData', data, { headers })
.then((response) => {
console.log("WITHIN RESPONSE")
commit('populateData',response.data)
})
.catch((error) => {
commit('populateData', 'Unable to obtain data, Error : ' + error)
})
}
}
then in component you can map getters and actions, and call them :
import { mapGetters, mapActions } from 'vuex'
computed: {
...mapGetters(['getData']),
},
methods: {
...mapActions(['performAction']),
async performAction() {
await this.setData(this.formData.value)
this.formData.returnValue = this.getData
}
}
You need to return your promise in your if you want to chain it in the calling method. eg:
getData ({ commit, state, dispatch }) {
const headers = { 'Content-Type': 'application/json' }
return this.$axios // now this promise will be returned and you can chain your methods together
.post('/getUrlData', state.data, { headers })
.then((response) => {
console.log("WITHIN RESPONSE")
commit('populateData',response.data);
return response.data; //this will allow you do send the data through to the next Then() call if you want to
})
.catch((error) => {
commit('populateData', 'Unable to obtain data, Error : ' + error)
})
}
This situation is a lot easier to manage with async-await IMO. It becomes:
export const actions = {
async getData ({ commit, state, dispatch }) {
const headers = { 'Content-Type': 'application/json' }
const response = await this.$axios.post('/getUrlData', state.data, { headers });
console.log("WITHIN RESPONSE")
commit('populateData',response.data);
}
}
and
methods: {
async performAction () {
// Set the value within the Vuex Store
this.$store.commit('modules/DataStore/populateData', this.formData.value)
// Perform the Action
await this.$store.dispatch('modules/DataStore/getData');
console.log("AFTER COMPLETE ACTION");
// Assign the update value to the variable
this.formData.returnValue = this.$store.state.modules.DataStore.data
}
}

Loading screen with react to improve UX

I am little confused about loading data approach.
So i want to show loading screen on page load until all data is loaded, my approach is to make for each fetch method on this page a loading state. here I am using redux with thunk to dispatch async action.
actions-types:
export const IS_LOADING_SKILLS = "IS_LOADING_SKILLS";
export const SET_SKILLS = "SET_SKILLS";
export const IS_LOADING_INTRO = "IS_LOADING_INTRO";
export const SET_INTRO = "SET_INTRO";
actions:
import { SET_SKILLS, SET_INTRO,IS_LOADING_INTRO,IS_LOADING_SKILLS} from "./actions-type"
export const fetchSkills = () => {
return (dispatch) => {
dispatch(isLoadingSkills(true));
await api.get(url)
.then(({ data }) => dispatch(setSkills(data)));
dispatch(isLoadingSkills(false));
}
}
export const fetchIntro = () => {
return (dispatch) => {
dispatch(isLoadingIntro(true));
await api.get(url)
.then(({ data }) => dispatch(setIntro(data)));
dispatch(isLoadingIntro(false));
}
}
const setSkills = (payload) => {
return {
type: SET_SKILLS,
payload: payload
}
}
const setIntro = (payload) => {
return {
type: SET_INTRO,
payload: payload
}
}
const isLoadingSkills = (payload)=>{
return{
type:IS_LOADING_SKILLS,
payload:payload
}
}
const isLoadingIntro = (payload)=>{
return{
type:IS_LOADING_INTRO,
payload:payload
}
}
state:
const InitialState ={
loading:{
loadingIntro:false,
loadingSkills:false
},
data:{
intro:"",
skills:[],
}
}
now when both loading state for each fetch data method become false the loading screen will disappear.
I want to know if this is a good approach or what is better please explain. thank you!
This is a good approach, but your code has some weird parts. Either use async/await or .then/.catch callbacks (don't forget about the .catch, you could dispatch a setErrorMessage action or so).
So, with promises you would do:
export const fetchSkills = () => {
return (dispatch) => {
dispatch(isLoadingSkills(true));
api.get(
.then(({ data }) => {
dispatch(setSkills(data)));
dispatch(isLoadingSkills(false));
})
.catch((error) => ...stuff)
}
}
export const fetchIntro = () => {
return (dispatch) => {
dispatch(isLoadingIntro(true));
api.get(url)
.then(({ data }) => {
dispatch(setIntro(data)));
dispatch(isLoadingIntro(false));
})
.catch((error) => ...stuff)
}
}
And with async/await you would do:
export const fetchSkills = () => {
return async (dispatch) => {
try {
dispatch(isLoadingSkills(true));
const { data } = await api.get(url)
dispatch(setSkills(data)));
dispatch(isLoadingSkills(false));
} catch(error) {
...stuff
}
}
}
export const fetchIntro = () => {
return async (dispatch) => {
try {
dispatch(isLoadingIntro(true));
const { data } = await api.get(url)
dispatch(setIntro(data)));
dispatch(isLoadingIntro(false));
} catch(error) {
...stuff
}
}
}
Remember that async/await is just syntactic sugar for promises. Instead of using .then and .catch on a promise you just define the function using await as async (which allows you to use await AND returns a promise), and you will need to try/catch to catch any errors that can happen whilst doing the request.

Im making my api call, but i am not able to return it back to my action creator

I have an action creator, it calls my apI(which is its own file), the api works, the action creator works as well. Im able to console.log my response from the api, my issue is returning it to my action creator, where the api function call was made. Thank You.
**ACTION CREATOR**
import * as types from './actionTypes';
import FootballApi from '../api/footballApi';
import { loadLeag_Success } from '.';
//type
export const LOAD_LEAG_GAMES_SUCCESS = 'LEAGUE_GAMES_SUCCESS';
export function loadLeag_Game_Success(data){
//console.log("before dis", data);
return;
};
export function getLeaguesGames(idArr, date) {
return function(dispatch) {
Promise.all(FootballApi.getLeaguesGamesAPI(idArr, date))
.then(() => {
// . I WANT TO RETURN IT HERE.
console.log("returned")
})
// .catch(error => {
// throw(error)
// })
};
}
**API FILE**
export default class FootballApi {
static getAllLeags() {
return fetch('https://apifootball.com/api/?APIkey=42f53c25607596901bc6726d6d83c3ebf7376068ff89181d25a1bba477149480&action=get_leagues').then(response => {
return response.json();
}).catch(error => {
return error;
});
}
static getLeaguesGamesAPI(idArr, date){
return idArr.map((id)=>{
return fetch(`https://apifootball.com/api/?APIkey=42f53c25607596901bc6726d6d83c3ebf7376068ff89181d25a1bba477149480&action=get_events&from=${date}&to=${date}&league_id=${id}`)
.then(res =>{
return res.json();
})
.then((game) => {
if(!game.error){
//console.log("game")
return game;
}
})
})
}
};
Assuming you already have the middleware(like thunk or saga) setup. Your Promise.All's resolve function doesn't take the data parameter.
Promise.all(FootballApi.getLeaguesGamesAPI(idArr, date))
.then((data) => {
dispatch(loadLeag_Game_Success(data));
});
Try doing this :
ACTION CREATOR
import * as types from './actionTypes';
import FootballApi from '../api/footballApi';
import { loadLeag_Success } from '.';
const LOAD_LEAG_GAMES_SUCCESS = 'LEAGUE_GAMES_SUCCESS';
export const loadLeag_Game_Success = (data) => {
console.log(data)// The data will be here
};
export function getLeaguesGames(idArr, date) {
return function(dispatch) {
Promise.all(FootballApi.getLeaguesGamesAPI(idArr, date))
.then((data) => {
dispatch(loadLeag_Game_Success(data));
})
.catch(error => {
throw(error)
})
}; }
I figured it out, i removed -
.then((game) => {
if(!game.error){
//console.log("game")
}
})
and put it in my action creator. everything works the way i need it. thanks.

react.js: Create resource with redux-form, rest api and async/await

I'm trying to create new resource with redux form and REST api.
I dispatch createPost action and I want to check if the post was succeeded before continue.
const handleFormSubmit = (values, dispatch) => {
dispatch(createPost(values));
//I want to check here if post was succeeded.
//if status = 200 this.props.history.push('/');
}
export function createPost(values) {
return async function(dispatch) {
let request;
try {
request = await axios.post(`${ROOT_URL}/posts`, values)
} catch(err) {
request = { err };
}
dispatch({
type: CREATE_POST,
payload: request
})
}
}
Return a promise, something like this :
export function createPost(values) {
return function(dispatch) {
return new Promise( async function(resolve, reject){
let request;
try {
request = await axios.post(`${ROOT_URL}/posts`, values)
} catch(err) {
reject(err)
}
dispatch({
type: CREATE_POST,
payload: request
})
resolve(request)
})
}
}
const handleFormSubmit = () => {
dispatch(createPost(values))
.then( res => {
// do yoour stuff on succes
} )
.catch(err => {
// stuff on err
})
}
As seeing your codes, I don't think you need to use promise.
Please try like following:
const getAction = (values) => (dispatch) => {
return axios
.post(`${ROOT_URL}/posts`, values)
.then(
() => {
dispatch({
type: CREATE_POST,
payload: request
})
},
() => {
throw new SubmissionError({
_error: 'Failed'
});
}
);
};
const handleSubmit = (values) => {
return dispatch(getAction(values));
};

Promise.all result fires before actions succeed

I'm searching and searching solution on Stackoverflow and Github but 1.5 day is not enough - I don't see where I'm making mistake. I have following code of my component where I would like to fetch data from two endpoints and after store updates I console log something.
class ExampleComponent extends Component {
componentDidMount(){
this.fetchData()
}
fetchData(){
const {dispatch} = this.props
Promise.all([
dispatch(getTodos()),
dispatch(getPhotos())
])
.then(() => {
console.log('I did everything!');
});
}
render() {
return (
<h1>Something</h1>
);
}
}
export default connect()(ExampleComponent)
And here are my actions...
export function getPhotos() {
return function(dispatch) {
axios.get('https://jsonplaceholder.typicode.com/photos')
.then((response) => {
console.log('photos')
dispatch({type: 'PHOTOS_REQUEST_SUCCESS',payload: response})
})
.catch((err) => {
dispatch(photosRequestError(err))
})
}
}
export function getTodos() {
return function(dispatch){
axios.get('https://jsonplaceholder.typicode.com/todos')
.then(
(response) => {
console.log('todos')
dispatch({type: 'TODOS_REQUEST_SUCCESS', payload: response})
})
.catch((err) => {
dispatch(todosRequestError(err))
})
}
}
In console I get "I did everything", "todos", "photos". How should I modify my code to update store first and then log "I did everything" in console?
Thanks in advance
You need to return the promises themselves so that the chaining apply synchronous, so try adding return to axios.get('endpoint/url') in both functions.

Categories