Redux connect chaining async actions - reactjs

I am in the process of trying to setup redux, react-redux and redux-thunk. Thinks are generally going well but I have a question about how things are supposed to look when chaining multiple async actions together.
Specifically I have a scenario where the actions can be invoked individually or indirectly by another action which can invoke them. My question is how should selectItem be authored if I want to be idiomatic?
action.js
export function fetchByContext(contextId) {
return dispatch => {
_fetchByContext(messages => {
dispatch({ type: RECEIVE_MESSAGES, ... });
});
};
};
export function subscribeByContext(contextId) {
return dispatch => {
_subscribeByContext(messages => {
dispatch({ type: RECEIVE_MESSAGES, ... });
});
};
};
export function selectItem(contextId) {
return dispatch => {
subscribeByContext(contextId)(dispatch);
fetchByContext(contextId)(dispatch);
};
};

I believe the key is that (ref):
Any return value from the inner function will be available as the return value of dispatch itself
If the inner functions of fetchByContext(), subscribeByContext() return a promise, these could be chained in series or run in parallel from selectItem(). An untested implementation, assuming neither _fetchByContext() nor _subscribeByContext() return a promise would be:
export function fetchByContext(contextId) {
return dispatch => {
return new Promise((resolve, reject) => {
_fetchByContext(messages => {
dispatch({ type: RECEIVE_MESSAGES, ... });
resolve(messages);
});
});
};
};
export function subscribeByContext(contextId) {
return dispatch => {
return new Promise((resolve, reject) => {
_subscribeByContext(messages => {
dispatch({ type: RECEIVE_MESSAGES, ... });
resolve(messages);
});
});
};
};
export function selectItem(contextId) {
return dispatch => {
// CALL IN SERIES
return dispatch(subscribeByContext(contextId))
.then(() => dispatch(fetchByContext(contextId)));
// CALL IN PARALLEL (alternative to the code above; this is probably not what you want - just keeping for reference)
return Promise.all(
dispatch(subscribeByContext(contextId)),
dispatch(fetchByContext(contextId))
);
};
}
Again please note the code above is untested, just in hope of giving an idea for the general solution.

Related

Async/await not working in a for-of loop defined in createAsyncThunk

I'm having trouble trying to get an async await to work inside a for loop when using createAsyncThunk. I expected that dispatch(performRunAllCells()) will call the API updateBrowser() synchronously for each cell in the editor.cells array in order. Instead, the dispatch resulted in updateBrowser() being called asynchronously all at once. What is happening here?
export const performRunAllCells = createAsyncThunk(
'editor/runAllCells',
async (_, { dispatch, getState, rejectWithValue }) => {
const { notebook: { selectedDataset } } = getState() as {notebook: {selectedDataset: string}};
const { editor } = getState() as {editor: EditorState};
try {
let results: DataEntity | undefined;
for (const cell of editor.cells) {
dispatch(setCellStatus({ id: cell.id, execStatus: '*' }));
results = await updateBrowser(selectedDataset, cell.editorContent);
dispatch(setCellStatus({ id: cell.id }));
}
return results;
} catch (e) {
return rejectWithValue(e.response.data);
}
},
);
Edit
Currently I'm testing updateBrowser() with a setTimeout:
export async function updateBrowser(selectedDataset: string, editorContent: string): Promise<DataEntity> {
return new Promise((resolve) => {
setTimeout(() => {
console.log('test');
resolve({
raw: editorContent, html: 'Test', console: 'Test',
});
}, 3000);
});
}
I was able to know if it's synchronous/asynchronous through the console log above. Currently, it is printing multiple "test" at once.
Nevermind. I made a mistake somewhere else and the code wasn't actually being called. It is working now after fixing it.

How to make dispatch with redux-thunk with axios

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.

Of Redux Thunk, Sequelize (Bluebird) - returning promise to action creator "chain"

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);
})
);

Should I use dispatch on return type of action in reactjs?

I confused on using dispatch.Please see my below code.
export function getUserInfo(isDeviceinfo) {
return (dispatch) => {
dispatch({
type: REQUEST_DEVICE_MODEL_RESET,
isDeviceinfo,
});
};
}
or
export function getUserInfo(isDeviceinfo) {
return => {
type: REQUEST_DEVICE_MODEL_RESET,
isDeviceinfo,
};
}
Now which one I should use.Please suggest me.
If you dont need to perform any asynchromous operation use this,
export function getUserInfo(isDeviceinfo) {
return{
type: REQUEST_DEVICE_MODEL_RESET,
isDeviceinfo,
};
}
If you need to perform asynchronous operation use dispatch.
function getUserInfo(isDeviceinfo) {
return (dispatch)=>{
//perform a async operation like this http call
return fetch(SOME_URL).then(j=>j.json()).then((d)=>{
dispatch({
type: REQUEST_DEVICE_MODEL_RESET,
isDeviceinfo,
})
})
}
}

How can I return a promise from my ActionCreator in React Redux

I have the following handler that dispatches two actions...
_onPress = () => {
let { request,userId} = this.props;
this.props.dispatch(acceptRequest(request.id,userId))
this.props.dispatch(navigatePop());
}
What I would like this to look like instead is the following...
_onPress = () => {
let { request,userId} = this.props;
this.props.dispatch(acceptRequest(request.id,userId))
.then(this.props.dispatch(navigatePop()))
}
My ActionCreator looks like this...
export function acceptRequest(requestId,fulfilledBy){
return dispatch => {
fulfillments.create(requestId,fulfilledBy)
.then(response => response.json())
.then(fulfillment => {
dispatch(_acceptRequestSuccess(fulfillment))
})
.catch(err => {
console.log(err);
dispatch(_acceptRequestError(error))
})
}
}
I am aware that they have many middleware(s) that people suggest, but I don't
see how any of them fit this scenario unless I am completed doing somthing
incorrectly.
In my particular case, I only want to dispatch the seond action if the first is
successful, but I don't want to do this from the action creator because then
it is less reusable.
Ok, so I misunderstood you, you should mention that you are using redux-thunk.
I don't think redux action should return a promise, I think you should do everything in the component and then play with the promise, like this:
_onPress = () => {
let { request,userId} = this.props;
fulfillments.create(requestId,fulfilledBy)
.then(response => response.json())
.then(fulfillment => {
this.props.dispatch(_acceptRequestSuccess(fulfillment))
})
.then(() => {
this.props.dispatch(navigatePop());
})
.catch(err => {
console.log(err);
this.props.dispatch(_acceptRequestError(error))
})
}
But if you still want to return a promise you can make the function inside the action return the promise:
export function acceptRequest(requestId,fulfilledBy){
return dispatch => {
return fulfillments.create(requestId,fulfilledBy)
.then(response => response.json())
.then(fulfillment => {
dispatch(_acceptRequestSuccess(fulfillment))
})
.catch(err => {
console.log(err);
dispatch(_acceptRequestError(error))
})
}
}

Resources