I use redux for dispatch an action creator (AC).
In this AC, I fetch some data to an external API (Melissa) with redux-thunk.
This AC works fine but if I try to capture this data that is set by the reducer in the store just after the call of the AC, this value is not already set.
How I can wait that my AC is finish to exucute the next line of my code ?
My AC is :
export function fetchMellisa (value) {
return dispatch => {
axios.get( /*URL*/ )
.then( ({ data }) => {
dispatch( { type: ACTION_CREATOR_1 , payload: data }
)
} ).catch( error => console.error( 'error', error ) );
};
}
and my call:
this.props.dispatch(fetchMellisa(values.currentAddress));
const currentAddress = this.props.addressMelissa[ 0 ].Address ;
this.props.addressMelissa[0].Address is my value set on the store by the reducer.
Try changing your thunk to return a Promise. This is a good rule to enforce on your thunks.
export function fetchMellisa(url) {
return dispatch =>
axios
.get(url)
.then(({ data }) => dispatch({ type: ACTION_CREATOR_1, payload: data }))
.catch(error => {
console.error("error", error);
return Promise.reject();
});
}
Then you can use it like this:
const url = values.currentAddress;
this.props.dispatch(fetchMellisa(url)).then(() => {
const newAddress = this.props.addressMelissa[0].Address;
});
the component should re-render with the new data in props, the api call should be in a lifecycle method such as componentDidMount or in an event handler. if you needed to use it inline, then you should return the axios.get and chain a .then on your original dispatch.
export function fetchMellisa (value) {
return dispatch => {
return axios.get( /*URL*/ )
.then( ({ data }) => {
dispatch( { type: ACTION_CREATOR_1 , payload: data }
)
} ).catch( error => console.error( 'error', error ) );
};
}
this.startLoadingMellisa();
this.props.dispatch(fetchMellisa(values.currentAddress))
.then( response => {
this.stopLoadingMellisa();
// do something that releases the loading state
})
if ( this.state.loading ) { return <SomeLoadingComponentOrMsg/> }
const currentAddress = this.props.addressMelissa[ 0 ].Address ;
i didn't create everything to save time but i think there is enough here to give a good picture.
Related
I am getting this error with TS and Redux and cant figure it out why it happens and how its could be fixed.
I am using redux thunk middleware.
Redux TS Error Actions must be plain objects. Instead, the actual type was: 'Promise'.
Seems that issue starts to happen in updateUserSession(data)
which gets started this way:
insertUserSession(data)
.then(
(res: IUserSession) => {
updateUserSession(data)
},
err => {
console.error("Insert session failed", err)
},
)
.then(() => {
//Here will continue another async action
})
export const insertUserSession = (session: IUserSession): Promise<IUserSession> => {
return axios.post("/user/" + session?.userId + "/session", session)
}
export const updateUserSession = (data: IUserSession) => {
return axios.put('/user/' + data.userId + '/session', data)
.then(
(response) => {
fetchUserSession(store.getState().user?.userData?.username)
.then(
(res) => {
if (typeof res.data === "object" && !Object.keys(res.data).length)
saveUserSession(res.data)
},
(err) => {
console.error(err);
},
)
},
(err) => {
console.error(err);
},
)
}
export const saveUserSession = (data: IUserSession) => {
return (dispatch: Dispatch<UserAction>) => {
dispatch({
type: UserType.SAVE_USER_SESSION,
payload: data,
});
}
}
This is happening because updateUserSession is actually a thunk and not an action. It seems saveUserSession is the action you need to call with callback data.
Be sure to declare the correct thunk signature:
const updateUserSession = (data: IUserSession) => (dispatch, getState) => {}
Also, don't forget to call dispatch to fire actions inside the thunk.
I'm trying my first react application with redux, along with Thunk as middle ware. When calling the action from one of the components, the action is hit but the code inside the action return is not executed. I have no clue what I'm missing here. I'm also using Firestore to get the data.
export const getBikeTypes = () => {
console.log('outside return')
return (dispatch, getState, { getFireBase, getFireStore }) => {
console.log('inside return')
const firestore = getFireStore();
firestore.collection('BikeTypes').get()
.then((response) => {
console.log(response)
return response
}).then(() => {
dispatch({ type: 'GET_BIKETYPES' });
}).catch((err) => {
dispatch({ type: 'GET_BIKETYPES_FAIL', err });
})
}
};
I think you should dispatch action with the payload once you get the response.
const firestore = getFireStore();
firestore.collection('BikeTypes').get()
.then((response) => {
dispatch({ type: 'GET_BIKETYPES', payload: response })
})
First, apologize for my english.
I'm trying to make a request with redux-thunk.... I dont understan it well.
My idea is make a request using axios but method return undefined before return value of request.
I dont know if I'm passing dispatch well.
Can you help me please? What am I doing wrong???
This is how use dispatch in my component:
....
const mapDispatchToProps = dispatch => {
return {
createCustomersGeoJSON: () => dispatch(createCustomersGeoJSON()),
getAdvicesData: hierarchy => dispatch(getAdvicesData(hierarchy)),
getSocialNetworkData: () => dispatch(getSocialNetworkData()),
dispatch,
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(injectIntl(CustomersWidget));
In actions I do this:
export const getSocialNetworkData = () => {
return dispatch => {
dispatch({
type: GET_SOCIAL_NETWORK_DATA,
payload: fetchSocialNetworkData(),
});
};
};
And this is the code of fetchSocialNetworkData function:
axios
.get(`http://localhost:5000/socialRoute`)
.then(data => {
let response = Object.assign({}, data);
if (
response &&
response.data &&
response.data.tweets &&
Array.isArray(response.data.tweets)
) {
console.log("data.tweets: ", response.data.tweets);
return response.data.tweets;
}
return [];
})
.catch(error => {
console.log("Error gettin data from socialRoute: ", error);
});
It's because you think you're returning the response but what you're actually returning is nothing because you've handled the result of the promise in a .then chain.
You have two options:
Return a promise and resolve it in the .then:
function fetchSocialNetworkData() {
return new Promise((resolve) => {
axios
.get(`http://localhost:5000/socialRoute`)
.then(data => {
let response = Object.assign({}, data);
if (
response &&
response.data &&
response.data.tweets &&
Array.isArray(response.data.tweets)
) {
console.log("data.tweets: ", response.data.tweets);
resolve(response.data.tweets);
}
resolve([]);
})
})
}
OR
Use async/await (the modern way)
async function fetchSocialNetworkData() {
const data = await axios.get(`http://localhost:5000/socialRoute`);
let response = Object.assign({}, data);
if (
response &&
response.data &&
response.data.tweets &&
Array.isArray(response.data.tweets)
) {
console.log("data.tweets: ", response.data.tweets);
return response.data.tweets;
}
return [];
}
Both of these are the same thing under the hood. IE they're both different ways of writing a promise.
Now. in your thunk, you're still just calling that function, which means you're going to get the unresolved promise rather than the result of that promise. So the thunk becomes:
export const getSocialNetworkData = () => {
return async (dispatch) => {
dispatch({
type: GET_SOCIAL_NETWORK_DATA,
payload: await fetchSocialNetworkData(),
});
};
};
The thing to take away from this is that you can get far without understanding promises but that lack of understanding will always be a ceiling for your JS skills.
I'm using Redux (+ thunk) to fetch data from my API. I implemented a Data Fetcher component that calls all the actions and once done, dispatches a LOADED. In my actual main component where I render content, I wait until isLoaded flag in the props is set to true.
Here's the method in the data fetcher:
const fetchData = () => {
if (isAuthenticated) {
const p = [];
p.push(fetchDataSetOne());
p.push(fetchDataSetTwo());
Promise.all(p).then( () => setHasLoaded() );
}
}
Each of those fetch methods returns an axios promise, in which I dispatch once retrieved like so:
export const fetchDataSetOne = () => dispatch => {
return axios.get(`${API_URL}/dataSetOne`)
.then(res => {
dispatch({
type: FETCH_ALL_DATA_SET_ONE,
payload: res.data.docs
});
});
};
In my component's render function I only render the content when loaded (which is set by the LOADED dispatch from the setHasLoaded action) like so:
{ hasLoaded && <MyContent> }
Even though I "wait" for the actions to finish (= Promise.all), my variable hasLoaded is set to true before the fetched data is set. Can anybody help?
The problem is you return a function NOT a promise.
This resolves immediately.
See working code sample
export const fetchData2 = dispatch => () => {
dispatch({type: 'START_FETCH'})
const p = [
fetchDataSetOne(dispatch),
fetchDataSetTwo(dispatch)
];
Promise.all(p).then((res) => setHasLoaded(res));
};
// this returns a promise AFTER it calls an action
const fetchDataSetOne = dispatch => {
return axois.get(`${API_URL}/dataSetOne`).then(res => {
dispatch({
type: "FETCH_ALL_DATA_SET_ONE",
payload: res.data.docs
});
});
};
This resolves after both promises are resolved, but the state updates after each promise is resolved. To update state after all promises resolve, try this:
export const fetchData3 = dispatch => () => {
dispatch({ type: "START_FETCH" });
const p = [
axois.get(`${API_URL}/dataSetOne`),
axois.get(`${API_URL}/dataSetTwo`)
];
Promise.all(p).then(callActions(dispatch));
};
const callActions = dispatch => res => {
dispatch({
type: "FETCH_ALL_DATA_SET_ONE",
payload: res[0].data.docs
});
dispatch({
type: "FETCH_ALL_DATA_SET_TWO",
payload: res[1].data.docs
});
setHasLoaded(res);
};
So similar to some previous posts referenced below, I'm trying to chain dispatch through Thunk, however my difficulty is on the return from Sequelize. I can see the MySQL query hit the DB and return data, however that return is not flowing through the action-creator to the subsequent .then
I presume it's the manner in which I'm trying to use Sequelize - I'm new to it.
Many thanks!
Code:
initDB.js
...
export function sequelizeQry(qryName: string) {
let query;
// For later query mapping
switch (qryName) {
case 'POSummary':
query = qry.spPOLedgerCardSummaryALL;
break;
default:
query = qry.spPOLedgerCardSummaryALL;
}
return new sequelize.Promise(
() => sequelize.query(query, { type: sequelize.QueryTypes.RAW })
.then((response) => {
console.log('Returning promise: ', response); //<-- This hits the console with data
return response;
})
);
}
database-actions.js
// #flow
import { fetchingQuery } from '../action-creators/database-creators';
const fetchQuery = (qryName: string) =>
(dispatch: *) => dispatch(fetchingQuery(qryName));
export default fetchQuery;
database-creators.js
// #flow
// Action Types
import { FETCH_QUERY } from '../constants/ActionTypes';
import { sequelizeQry } from '../utils/initDB';
/** Action-creators */
export function fetchingQuery(qryName: string) {
const actionType = FETCH_QUERY;
return (dispatch: *) => {
dispatch({ type: `${actionType}_PENDING` });
sequelizeQry(qryName) //<-- This gets called
.then((payload) => dispatch({ //<-- Don't seem to get here
type: `${actionType}_FULFILLED`,
payload
}))
.catch(err =>
// Dispatch the error action with error information
dispatch({
type: `${actionType}_REJECTED`,
error: err
})
);
};
}
Some other references I've checked:
Redux thunk: return promise from dispatched action
return promise from store after redux thunk dispatch
All credit goes to adrice727.
Here's the code change for future visitors:
...
return new sequelize.Promise(
() => sequelize.query(query, { type: sequelize.QueryTypes.RAW })
.then((response) => {
console.log('Returning promise: ', response); //<-- This hits the console with data
return response;
})
);
...
// BECOMES
return new sequelize.Promise(
(resolve) => sequelize.query(query, { type: sequelize.QueryTypes.RAW })
.then((response) => {
console.log('Returning promise: ', response);
return resolve(response);
})
);