Proper way to call two async action - javascript

How to call an async action after another action was successfully dispatched?
I am learning Redux and I have some questions, about async action.(I am using thunk)
I have two action:
export const addToCart = addToCartData => dispatch => {
axios.post("/api/cart/add-to-cart", {addToCartData)
.then(res => {
dispatch({ type: ADD_TO_CART, payload: res.data });
})
.catch(err => console.log(err));
};
export const removeProduct = (userID) => dispatch => {
axios
.delete(`/api/wait-list/remove/${userID}`)
.then(res => {
dispatch({ type: REMOVE_FROM_WAITLIST, payload: res.data });
})
.catch(err => console.log(err));
};
And I want to execute removeProduct action only after addToCart will be successfully executed! I am trying to do third one with two of them, it looks like this:
export const addToCartAndPemoveProduct = (data) => dispatch => {
dispatch(addToCart(data)
dispatch(removeProduct(data));
But it executes removeProduct action first, and after addToCart....
How do I can do it right due to order? Maybe I should return a promise from first one and execute second one after it will be successfull resolve? It will be looking like this:
export const addToCart = addToCartData => dispatch => {
return axios.post("/some", {addToCartData)
.then(res => { dispatch({ type: ADD.....})
};
export const addToCartAndPemoveProduct = (data) => dispatch => {
dispatch(addToCart({ userID, productId }))
.then(data => {
dispatch(removeProduct({ userID, productName, productDescr }));
})
}
Is it ok or not?

Do you always want to dispatch removeProduct after addToCart? In this case:
export const addToCart = addToCartData => dispatch => {
// You'll have to get the userId here first, probably from getState() if it's not being passed in.
axios.post("/api/cart/add-to-cart", {addToCartData)
.then(res => {
dispatch({ type: ADD_TO_CART, payload: res.data });
dispatch(removeProduct(userId));
})
.catch(err => console.log(err));
};
Maybe rename the action to moveProductFromWaitListToCart to express the full transaction.

Related

How to change a query value on an api call based on Redux

I have this issue that i do not know how to figure it out, this action.js is doing an api call to my app from weatherApi, what I need is make a button to change cityName value to another as 'madrid' for example, another button to change lang value. How could i do it from app.js or can I get it done on the same file action.js
const appid = 'randomapi';
const cityName = 'london';
const lang = 'en';
export const fetchData = () => {
return (dispatch) => {
return fetch(`https://api.openweathermap.org/data/2.5/weather?q=${cityName}&lang=${lang}&appid=${appid}`)
.then(response => response.json())
.then(json => dispatch(
{ type: "FetchData", data: json }))
.catch(err => dispatch(
{ type: "ERROR",msg: "Unable to fetch data" }))
}
}

How to pass return value from component to another one

I have a const that assembles a get (in another component) and returns me a verification code, this is: response.data.verifyCode
This component is called through a submit on another component.
I need to get this value in my another const, which is below:
export const sendCode = (id, username) => (dispatch) => {
dispatch({ some code here });
return registerAccount
.sendCode(id, username)
.then((response) => {
dispatch({ payload: response.data.verifyCode });
return response.data;
})
.catch(() => {
return null;
});
};
export const getCodeAndVerify = (id, userCode) => (dispatch) => {
dispatch({ some code here });
const getVerifyCode = // I need to get response.data.verifyCode from sendCode above
// I try to use
// const getVerifyCode = { verifyCode: sendCode() };
// but this returns [object object]
return registerAccount
.getCodeAndVerify(id, userCode, getVerifyCode)
.then(() => {
// some code here
})
.catch(() => {
// some code here
});
};
That is, I need to get the verifyCode from the return from the superior const and use it in the other const, but I'm not sure how to do that. Can someone help me?
Asynchronous actions (I'm assuming thunks) also receive a getState second argument after dispatch. Assuming there's a reducer to handle the verifyCode send code success, you can access the store and retrieve the verifyCode value in getCodeAndVerify.
export const sendCode = (id, username) => (dispatch) => {
dispatch({ some code here });
return registerAccount
.sendCode(id, username)
.then((response) => {
dispatch({
type: 'VERIFY_CODE_SUCCESS', // <-- action object needs type
payload: response.data.verifyCode,
});
return response.data;
})
.catch(() => {
return null;
});
};
export const getCodeAndVerify = (id, userCode) => async (dispatch, getState) => {
dispatch({ type: TYPES.PASS_CREATION_REQUESTED });
const getVerifyCode = getState().path.to.verifycode; // <-- retrieve from state
return registerAccount
.getCodeAndVerify(id, userCode, getVerifyCode)
.then(() => {
// some code here
})
.catch(() => {
// some code here
});
};

Redux action refuse to work with a function but work inline

I'm new to Redux and JavaScript and came across this now,
The line dispatch({type: 'REQUEST_START'}); is working but when I write like this:
dispatch(requestStart);
Then no action are fired!
Any idea? This is working in another app so It's something I have missed
I have this code:
import { booksActionTypes } from './books.types';
export const requestStart = () => ({
type: booksActionTypes.REQUEST_START,
});
export const requestSuccess = books => ({
type: booksActionTypes.REQUEST_SUCCESS,
payload: books,
});
export const requestFailure = errMsg => ({
type: booksActionTypes.REQUEST_FAILURE,
payload: errMsg,
});
export const actionCreators = {
// "applicationUrl": "http://localhost:51374", http://erikswed.ddns.net:8965/api/BooksXml/getbooks/fromall/?title=dep&author=&genre=&price=
// "sslPort": 44378
requestBooks: (book) => async (dispatch, getState) => {
dispatch({type: 'REQUEST_START'});
var queryString = Object.keys(book)
.map((key) => {
return encodeURIComponent(key) + "=" + encodeURIComponent(book[key]);
})
.join("&");
var url = "http://erikswed.ddns.net:8965/api/BooksXml/getbooks/fromall/?" + queryString;
console.log(`url: `, url);
await fetch(url)
.then((res) => res.json())
.then((booksList) => {
dispatch(requestSuccess, booksList);
})
.catch((rejected) => {
console.log(rejected);
dispatch(requestFailure, rejected);
});
// const response = await fetch(url);
// const booksList = await response.json();
},
};
requestStart is a function (that returns an object), therefore you need to invoke it - dispatch(requestStart())
or make it an object, which you don't need to invoke
export const requestStart = {
type: booksActionTypes.REQUEST_START,
};
...
dispatch(requestStart)
when you use a function, usually called an action creator, its useful for passing args for them to be added to the action (the object). Its probably best to keep the function, but just invoke it so you have consistency across your actions/action creators

How to fire a second function after a triggering a function in a functional component (React, React Hooks)

So I have a button:
<button onClick={doSomething}>Do it</button>
And I have a function
const doSomething = () => {
console.log(1)
}
And I want console.log(2) to fire after triggering doSomething.
Something like this:
const doSomething = () => {
console.log(1)
console.log(2)
}
But this one fires console.log(2) immediately. What I want is to fire console.log(2) after console.log(1), when clicking on the button.
Do I need to use useEffect() here? If yes, how?
EDIT:
Here is the problem. getQuestions() fires immediately when the function a runs. I want getQuestions() to fire after props.answerQuestion() finished.
const a = (e) => {
e.preventDefault();
props.answerQuestion({
question: question,
answer: answer,
});
getQuestions();
};
EDIT2:
export const answerQuestion = (answerQuestion) => (dispatch) => {
const token = localStorage.getItem("token");
if (token) {
axios
.post("http://localhost:5000/answerQuestion", answerQuestion, {
headers: {
"X-Auth-Token": token,
},
})
.then((res) => {
dispatch({
type: ANSWER_QUESTION,
payload: res.data,
});
});
}
};
You can JavaScript Promises for this problem. Using Promises in answerQuestion() Function will let you use to chain .then() and .catch() method in a function
export const answerQuestion = (answerQuestion) => (dispatch) => return new Promise((resolve, reject) => {
const token = localStorage.getItem("token");
if (token) {
axios
.post("http://localhost:5000/answerQuestion", answerQuestion, {
headers: {
"X-Auth-Token": token,
},
})
.then((res) => {
dispatch({
type: ANSWER_QUESTION,
payload: res.data,
});
resolve();
})
.catch((error) => {
reject(error);
})
}
});
const a = (e) => {
e.preventDefault();
props.answerQuestion({
question: question,
answer: answer,
})
.then(() => {
getQuestions();
})
.catch((error) => {
console.log(error)
})
};
You wouldn't need to use useEffect in this scenario, all you want to do is wait for the api call to resolve before calling getQuestions. one way you can accomplish this is by:
// update answerQuestion into an async function
export const answerQuestion = async (answerQuestion) => async (dispatch) => {
const token = localStorage.getItem("token");
if (token) {
const response = await axios // <--- add await here
.post("http://localhost:5000/answerQuestion", answerQuestion, {
headers: {
"X-Auth-Token": token,
},
})
await dispatch({
type: ANSWER_QUESTION,
payload: response.data,
});
}
};
then in your component:
const a = async (e) => {
e.preventDefault();
await props.answerQuestion({
question: question,
answer: answer,
});
getQuestions();
};

What is the better/correct way of using Promise.all with React-Redux-Thunk?

export const FETCH_DB_BEGIN = 'FETCH_DB_BEGIN'
export const FETCH_DB_SUCCESS = 'FETCH_DB_SUCCESS'
export const FETCH_DB_FAILURE = 'FETCH_DB_FAILURE'
export const fetchDatabase = () => {
return dispatch => {
const profile_url = 'localhost:5000/profiles'
const release_url = 'localhost:5000/releases'
const emp_url = 'localhost:5000/users'
let promises = []
let options = {
headers: header,
method: 'get',
mode: 'cors',
body: null,
}
dispatch(fetchDbBegin());
// run the script async. change state when it's done.
let profile_promise = new Promise((resolve, reject) => {
fetch(profile_url, options)
.then(res => res.json())
.then(resText => {
// Use Dispatch Here?
})
}).catch(err => {
console.log(err)
})
promises.push(profile_promise)
// run the script async. change state when it's done.
let release_promise = new Promise((resolve, reject) => {
fetch(release_url, options)
.then(res => res.json())
.then(resText => {
})
}).catch(err => {
console.log(err)
})
promises.push(release_promise)
// run the script async. change state when it's done.
let emp_promise = new Promise((resolve, reject) => {
fetch(emp_url, options)
.then(res => res.json())
.then(resText => {
})
}).catch(err => {
console.log(err)
})
promises.push(emp_promise)
Promise.all(promises).then(values => {
console.log(values)
})
}
}
export const fetchDbBegin = () => ({
type: FETCH_DB_BEGIN
});
export const fetchDbSuccess = (data) => ({
type: FETCH_DB_SUCCESS,
payload: { data }
});
export const fetchDbFailure = (err) => ({
type: FETCH_DB_FAILURE,
payload: { err }
});
I am in a process of refactoring a React class component to use Redux. It initially had all API calls inside the componentDidMount and it was so messy.
I am using redux-thunk to move this out from the class component.
The fetchDatabase in my databaseAction.js does everything that componentDidMount did in the class component.
Normally if it was a single API call, I would have just dispatched the fetchDbSuccess as the API call was done successfully. However, using Promise.All which takes three async API calls, I am not sure whether I should
create a separate action for each API call (fetchProfileSuccess, fetchReleaseSuccess, and fetchUserSuccess) and dispatch each one of them at the end of each Promise (the place where I put //Use Dispatch Here? in the code.
OR
Just dispatch single fetchDbSuccess when the Promise.all gets resolved.
If I choose to do 2, am I supposed to update all three states in my reducer?
Thanks
You should only dispatch and update state if you have code that cares about said state updates. For example, if you're just wanting to show a single spinner then have the spinner go away when fully completed, your user doesn't necessarily care about each atomic operation, so you don't need it reflected in state. If you have a UI that does show each, then you would want those extra dispatches.
By the way, your Promises look a bit overcomplicated. If you decide you don't need those extra state changes, you can simplify to this:
export const FETCH_DB_BEGIN = 'FETCH_DB_BEGIN'
export const FETCH_DB_SUCCESS = 'FETCH_DB_SUCCESS'
export const FETCH_DB_FAILURE = 'FETCH_DB_FAILURE'
export const fetchDatabase = () => {
return dispatch => {
dispatch(fetchDbBegin());
const urls = [
'http://localhost:5000/profiles',
'http://localhost:5000/releases',
'http://localhost:5000/users'
];
const options = {
headers: header,
method: 'get',
mode: 'cors',
body: null,
}
const fetchJson = url => fetch(url, options).then(res => res.json());
Promise.all(urls.map(fetchJson))
.then(([profile, release, employee]) => {
dispatch(fetchDbSuccess({ profile, release, employee }));
})
.catch(err => {
dispatch(fetchDbFailure(err));
});
}
}
export const fetchDbBegin = () => ({
type: FETCH_DB_BEGIN
});
export const fetchDbSuccess = (data) => ({
type: FETCH_DB_SUCCESS,
payload: { data }
});
export const fetchDbFailure = (err) => ({
type: FETCH_DB_FAILURE,
payload: { err }
});

Categories