Awaiting dispatch actions to end - reactjs

is it possible to await for dispatch action to end, so then, with that dispatched data before, I could use it in another function?
Currently I have this type of code, but I believe I shouldn't pass request to action and I should use
const url = '/someurl';
return (dispatch) => {
apiClient.get(url)
.then((res) => {
dispatch({
type: FETCH_SESSION_INFO,
payload: res,
});
});
};
instead of:
const url = '/someUrl';
const request = apiClient.get(url);
return {
type: FETCH_SESSION_INFO,
payload: request,
};
I want to do this:
fetchSessionInfo.then(() => doSomething(session.something))
where session.something is data we've just fetched from fetchSessionInfo, but when trying to use 1st version I get an information, that fetchSessionInfo is undefined

Related

React Redux Call action within action and get return value

I am trying to call and action from an action to get a database record by name, then I want to use the ID of the role record in the SignUp action that is currently being called .
How can I reuse the code for my GetRolebyName action from within the sign up action, I was trying to avoid doing the same APi request in two places.
Essentially I am just trying to look up the RoleId when creating a user.
Role actions:
export const fetchRoleByName = name => async dispatch => {
const response = await db.get(`/roles?name=${name}`);
dispatch({
type: FETCH_ROLE,
payload: response.data[0]
});
};
Sign up Action:
export const signUp = values => async (dispatch, getState) => {
const role = await dispatch(fetchRoleByName(values.userType))
const response = await db.post('/users/',
{
...values,
roleId: role.id
}
);
dispatch({
type: SIGN_UP,
payload: response.data
});
history.push('/');
};
Solution:
As pointed out by Kaca992, the fetchRoleByName action never actually returned anything so the change required was as per below;
export const fetchRoleByName = name => async dispatch => {
const response = await db.get(`/roles?name=${name}`);
const data = response && response.data && response.data[0];
dispatch({
type: FETCH_ROLE,
payload: data
});
return data;
};
Inside fetchRoleByName just return response. Return from dispatch is the return value of the inner function:
export const fetchRoleByName = name => async dispatch => {
const response = await db.get(`/roles?name=${name}`);
dispatch({
type: FETCH_ROLE,
payload: response.data[0]
});
return response; (or return response.data[0] if that is the role object you want, but then I would recommend writing it like this: response && response.data && response.data[0] just in case of hitting an unexisting value from db)
};
Hope this helps.

Map over Axios Response and append 2nd Axios Response to 1st

I'm creating a movie search React App using axios and the TMDb API. I want to make an axios.get call to search for a movie based on query, then map over the response, and pass the movie.id for each movie into a 2nd axios call to get more details for each movie.
I have tried many approaches, but I'm confused about how I need to return the values, or if I need to use async/await at some point in the code to wait for responses. I don't really know the best way to go about this - there's got to be a simple way to do it in sequence, but the asynchronous nature of the problem makes this difficult for me.
I thought it would be simpler to make my first axios call inside my search function, and then write a second function getDetails(id) that takes a movie id and makes an axios call with it.
Ideally, if I call the getDetails function in a map, get a response, append that response to my movies array, I could easily have an array of movies with appended details to each individual movie. I could then store the entire array in state, and use a single piece of state to manage all my movie data. I know that this process is not synchronous and that axios is promise based, but is there a way to go about this sequentially or am I approaching it all wrong? I know that .push() is not working in this situation, but I have tried other methods and they have always returned undefined, a promise, or an unintended result.
const search = query => {
axios
.get(
`https://api.themoviedb.org/3/search/movie?api_key=${API_KEY}&language=en-US&query=${query}&page=1&include_adult=false`
)
.then(response => {
response.data.results.map(movie => {
const details = getDetails(movie.id);
return movie.push(details);
});
});
};
const getDetails = id => {
axios
.get(
`https://api.themoviedb.org/3/movie/${id}?api_key=${API_KEY}&language=en-US`
)
.then(response => {
return response.data;
});
};
EDIT 1: I've played with the answer by Abanoub Istfanous, and it seemed to return undefined with the added dispatch to update my state. I tweaked it more and the second set of code marked WORKING seems to be displaying some search results, but I cannot render any of the appended data from getDetails to the screen, as they are showing undefined. Am I still doing something incorrectly?
NOT WORKING:
const search = async query => {
dispatch({
type: MOVIE_SEARCH_REQUEST
});
const response = await axios.get(
`https://api.themoviedb.org/3/search/movie?api_key=${API_KEY}&language=en-US&query=${query}&page=1&include_adult=false`
);
const data = Promise.all(
response.data.results.map(async movie => {
movie.details = await getDetails(movie.id);
})
);
dispatch({
type: MOVIE_SEARCH_COMPLETE,
payload: data
});
return data;
};
// getDetails is unchanged...
WORKING:
const search = async query => {
dispatch({
type: MOVIE_SEARCH_REQUEST
});
const response = await axios.get(
`https://api.themoviedb.org/3/search/movie?api_key=${API_KEY}&language=en-US&query=${query}&page=1&include_adult=false`
);
const data = Promise.all(
response.data.results.map(async movie => {
movie.details = await getDetails(movie.id);
})
);
dispatch({
type: MOVIE_SEARCH_COMPLETE,
payload: response.data.results
});
return data;
};
// getDetails is unchanged...
I recommend to use Promise
/*
*#return data
*/
const getDetails = async (id) => {
const response = await axios
.get(
`https://api.themoviedb.org/3/movie/${id}?api_key=${API_KEY}&language=en-US`
)
return response.data
};
then
const search = async (query) => {
const response = await axios
.get(
`https://api.themoviedb.org/3/search/movie?api_key=${API_KEY}&language=en-US&query=${query}&page=1&include_adult=false`
);
const data = Promise.all(response.data.results.map(async movie => {
movie.details = await getDetails(movie.id);
}))
return data
};
The reason why it returns undefined is because Promise.all() will be executed asynchronously on the job queue and will only return the result after all Javascript gets executed and the call stack gets empty. So a simple solution to your code will be placing an await before Promise.all().
const search = async query => {
dispatch({
type: MOVIE_SEARCH_REQUEST
});
const response = await axios.get(
`https://api.themoviedb.org/3/search/movie?api_key=${API_KEY}&language=en-US&query=${query}&page=1&include_adult=false`
);
const data = await Promise.all(
response.data.results.map(async movie => {
movie.details = await getDetails(movie.id);
})
);
dispatch({
type: MOVIE_SEARCH_COMPLETE,
payload: response.data.results
});
return data; //No need to return data since you are dispatching it to redux store
};
You can read more about the asynchronous nature of JS on browsers here:
https://medium.com/#Rahulx1/understanding-event-loop-call-stack-event-job-queue-in-javascript-63dcd2c71ecd

Why are action function called wrapped inside dispatch in react?

I was just going through some react-redux code online and basically i came across the following set of code , which is basically a js file full of ACTIONS , just to give some context a combination of redux and redux-thunk is being used here:
export const init = () => async dispatch => {
dispatch({ type: TYPES.SET_LOADING });
await dispatch(getConfig());
await dispatch(getGenres());
dispatch({ type: TYPES.REMOVE_LOADING });
};
// Action Creator to get the config object from the API
export const getConfig = () => async dispatch => {
const res = await tmdbAPI.get('/configuration');
dispatch({
type: TYPES.GET_CONFIG,
payload: res.data,
});
};
I am a bit confused as to why is getConfig function is being wrapped inside a dispatch ?
if you don't want, don't wrap that
like this
export const init = () => async dispatch => {
dispatch({ type: TYPES.SET_LOADING });
dispatch(await getConfig());
dispatch({ type: TYPES.REMOVE_LOADING });
};
//this is not using redux-thunk
// Action Creator to get the config object from the API
export const getConfig = async () => {
const res = await tmdbAPI.get('/configuration');
return {
type: TYPES.GET_CONFIG,
payload: res.data,
};
};
Important
but there are so many reasons why wrap the actions by dispatch.
Here is one example.
when you want get multiple data in one action step by step
export const getData = () => async dispatch => {
dispatch({
type: DATA_LOADING_START
});
try {
const res = await tmdbAPI.get('/url1');
dispatch({
type: DATA1_LOADED,
payload: res.data,
});
const res = await tmdbAPI.get('/url2');
dispatch({
type: DATA2_LOADED,
payload: res.data,
});
} catch (err) {
// handle error
}
dispatch({
type: DATA_LOADING_END
});
};
//this is using redux-thunk
#AlexanderSolonik
Question: Why wrap actions by dispatch?
because dispatch() sends the action result to the reducer.
Redux thunks are just redux actions that can perform side effects. So
export const init = () => async dispatch => {
dispatch({ type: TYPES.SET_LOADING });
await dispatch(getConfig());
await dispatch(getGenres());
dispatch({ type: TYPES.REMOVE_LOADING });
};
Is just an async init function which when called performs the steps in a synchronous manner.
The key is that thunks can dispatch other thunks/actions/etc so the init thunk is just dispatching the getConfig() action which is itself async so the next step of the init function won't be performed until the config API call finishes (possibly because some other code depends on it)

Creating Components based on multiple axios requests

I'm working on redux-thunk project which i need to reach out endpoint-1 to get which & how many components i need to show in a page. Then i'm required to use this return to reach out endpoint-2 and get the data with the parameters from the enpoint-1.
I'm having hard time to create the correct logic. Also i'm sharing a diagram which i hope gives you the idea and requirements.
Flow Diagram
Thanks
export const fetchByPage = () => async dispatch => {
const response = await streams.get("byPage?", {
params: parameters.byPageParams
});
console.log(response.data);
dispatch({ type: FETCH_BY_PAGE, payload: response.data });
};
export const fetchPersons = () => async dispatch => {
const response = await streams.get(URL, {
params: parameters.byCriteriaParams
});
dispatch({ type: FETCH_PERSONS, payload: response.data });
};
Here are my actions. I'm trying to update byCriteriaParams with the data returns from fetchByPage call.

Fetching data from store if exists or call API otherwise in React

Let's assume I have a component called BookOverview that displays details of a book.
I'm getting data with an action:
componentDidMount() {
this.props.getBook(areaId);
}
And then I get the data with axios:
export const getBook = () => async dispatch => {
const res = await axios.get(
`${API}/${ENDPOINT}`
);
dispatch({
type: GET_BOOK,
payload: res.data
});
};
How shall I change this code to:
if redux store already have the book loaded - return it
if no book is present in the store - call the relevant API?
What is the best practise to achieve that please?
You can have the getState inside your async action creator like this:
export const getBook = () => async (dispatch, getState) => {
if(!getState().book /* check if book not present */) {
const res = await axios.get(
`${API}/${ENDPOINT}`
);
dispatch({
type: GET_BOOK,
payload: res.data
});
} else {
dispatch({
type: GET_BOOK,
payload: getState().book
});
}
};
For More Async Actions-Redux
You can try it this way:
componentDidMount() {
if(this.props.book==null){
this.props.getBook(areaId);
}
}
I assumed that you have a property called book in your props. that populates from the particular reducer.
You have to subscribe the particular reducer to get the this.props.book - This gives the value that you have in your store.

Resources