I'm trying to call an existing function that makes an API request using axios in another function which also makes an API request but its unable to execute the function called. I'm relatively new to axios and react so I'm not sure if I'm missing something here and your help would be much appreciated. Also please note, I can call the API again via axios in the second function but instead of repeating the code I would rather like to do function call.
Function 1:
export const getUsers = () => async (dispatch) => {
try {
const resp = await axiosInstance.get("/api/users/");
dispatch({ type: GET_USERS, payload: resp.data });
} catch (error) {
dispatch(returnErrors(error.response.data, error.response.status));
}
}
Function 2:
export const deactivateUser = (userID) => async (dispatch) => {
try {
const res = await axiosInstance.put(`/api/user/disable/${userID}/`);
dispatch(createMessage({ deactivateUser: res.data }));
} catch (error) {
dispatch(returnErrors(error.response.data, error.response.status));
}
}
What I'm trying to achieve is following:
export const deactivateUser = (userID) => async (dispatch) => {
try {
const res = await axiosInstance.put(`/api/user/disable/${userID}/`);
dispatch(createMessage({ deactivateUser: res.data }));
getUsers(); // This is not getting called....
} catch (error) {
dispatch(returnErrors(error.response.data, error.response.status));
}
}
Any advice or help would be much appreciated!
Related
I need function in redux-toolkit to fetch all data from others slices.
I have this code:
export const getAllData = createAsyncThunk(
'fetchRoot/getAllData',
async (_, { dispatch, rejectWithValue }) => {
const promises = [dispatch(getUsers()), dispatch(getSettings()), dispatch(getClients())];
Promise.all(promises)
.then((res: any) => {
// for (const promise of res) {
// console.log('SSS', promise);
// if (promise.meta.rejectedWithValue) {
// return rejectWithValue(promise.payload);
// }
}
})
.catch((err) => {
console.log(err);
});
}
);
My question: if one of slice fetch function (example: getUsers()) is rejected, how to reject promise.all?
getUsers() function and extraReducers:
export const getUsers = createAsyncThunk('users/getUsers', async (_, { rejectWithValue }) => {
try {
const res = await agent.Users.getAll();
return await res.data;
} catch (err) {
return rejectWithValue(err);
}
});
extraReducers: (builder) => {
builder
// GetUsers lifecycle ===================================
.addCase(getUsers.pending, (state) => {
state.apiState.loading = true;
state.apiState.error = null;
})
.addCase(getUsers.fulfilled, (state, { payload }) => {
state.apiState.loading = false;
state.data = payload;
})
.addCase(getUsers.rejected, (state, { payload }) => {
state.apiState.loading = false;
state.apiState.error = payload;
})
You have it basically right. Once the Promise.all(promises) has resolved you will have an array containing the resolved value of each of your individual thunks.
The individual promises will always resolve and will never reject. They will resolve to either a fulfilled action or a rejected action. In some cases, it will make sense to use the unwrap() property which causes rejected actions to throw errors. But looking at the .meta property will work too.
You can check your action with the isRejected or isRejectedWithValue functions which serve as type guards, that way you won't have any TypeScript errors when accessing properties like action.meta.rejectedWithValue.
The hard part here is trying to return rejectWithValue() from inside a loop. I would recommend unwrapping to throw an error instead.
import { createAsyncThunk, unwrapResult } from "#reduxjs/toolkit";
export const getAllData = createAsyncThunk(
"fetchRoot/getAllData",
async (_, { dispatch }) => {
const promises = [dispatch(getUsers()), dispatch(getSettings()), dispatch(getClients())];
const actions = await Promise.all(promises);
return actions.map(unwrapResult);
}
);
Note that there is no reason to try/catch in your getUsers thunk if you are going to rejectWithValue with the entire caught error object. Just let the error be thrown.
export const getUsers = createAsyncThunk('users/getUsers', async () => {
const res = await agent.Users.getAll();
return res.data;
});
I'm having some troubles about react, fetch and async/await.
I have this code as a fetch wrapper:
export const fetcher = (url, options = null) => {
const handle401Response = error => {
throw error;
}
const isResponseValid = response => {
if (!response.ok)
throw response;
else
return response.json();
}
return fetch(url, { ...options, credentials: 'include' })
.then(isResponseValid)
.catch(handle401Response);
}
Then I define some API calls functions like:
export const getGroups = (id = null) => {
return fetcher(`${API_GROUP_URL}${id !== null ? `?id=${id}` : ''}`);
}
And then I try to use it like:
export function SomeComponent(props) {
const groups = async () => {
try {
const ret = await getGroups();
return ret;
} catch (err) {
console.log(err);
}
};
console.log(groups());
return <h1>Component</h1>
}
The result in console is: Promise{}.
I have read docs about async/await but can't understand why await is not waiting for promise to end.
Thanks in advance!
export function SomeComponent(props) {
const [data, setData] = useState()
const groups = async () => {
};
useEffect(() => {
const fetchData = async () => {
try {
const ret = await getGroups();
// process and set data accordingly
setData(ret)
} catch (err) {
console.log(err);
}
}
// fetch data inside useEffect
fetchData()
}, [])
// console.log(groups());
return <h1>Component {data?.prop}</h1>
}
Hope this gives you an idea on how to fetch in a functional component
Async functions always return a promise. The time when you call that function it will give you back a promise instantly. You have used await inside the function and it is waiting for getGroup promise.
In normal javascript function console.log(await) this will fix the issue but in react you have to do it inside a another function because you cant make react components async (at least not in React 17 and below)
i want to know if there is some clean code or update to make it on my code, because i think i repeat the same code on every actions on my redux, my question is how can I avoid calling axios on my actions files ?
Please take a look on my code here :
export const SignInType = (host, lang) => async (dispatch) => {
try {
dispatch({
type: USER_LOGIN_SIGNINTYPE_REQUEST,
});
const { data } = await axios.get(
`/${lang}/data?host=${host}`
);
console.log({ data });
dispatch({
type: USER_LOGIN_SIGNINTYPE_SUCCESS,
payload: data,
});
dispatch({
type: USER_LOGIN_CLEAR_ERROR,
});
} catch (err) {
dispatch({
type: USER_LOGIN_SIGNINTYPE_FAIL,
payload: err,
});
}
};
I Really want to delete the Axios name from my actions file and make it on a separate file, but how can i do this ?
Thank you
We can suggest but there's no correct answer to this, initially any redundant lines of code can be abstracted, so in order to make things a little bit easier, we need to abstract the obvious and add the meaningful, e.g:
abstract the way you write action creators:
const actionComposer = (options) => (...args) => async dispatch => {
const modifiedDispatch = (type, payload) => dispatch({ type, payload });
const { action, onSuccess, onFailed } = options(modifiedDispatch);
try {
if (action) {
const res = await action(...args)
onSuccess(res);
}
} catch (err) {
onFailed(err)
}
}
then your code can look like this:
export const SignInType = actionComposer((dispatch)=> {
return {
action: async (host, lang) => {
dispatch(USER_LOGIN_SIGNINTYPE_REQUEST);
const { data } = await axios.get(`/${lang}/data?host=${host}`);
return data;
},
onSuccess: (res) => {
dispatch(USER_LOGIN_SIGNINTYPE_SUCCESS, data);
dispatch(USER_LOGIN_CLEAR_ERROR);
},
onFailed: (err) => {
dispatch(USER_LOGIN_CLEAR_ERROR, err.message)
}
}
})
Redux Toolkit already has a createAsyncThunk API that does all the work of defining the action types and dispatching them for you. You should use that.
Alternately, you can use our RTK Query data fetching and caching library, which will eliminate the need to write any data fetching logic yourself.
im trying to make a request to google api but returns me network error. If i put the url in the browser, brings me the information correctly.I tryed to formate the request without success. The google places search works correctly too.
export const fetch_information = (skip, limit, filter) => async (dispatch) => {
try {
var url = `https://maps.googleapis.com/maps/api/place/details/json?place_id=ChIJk0aJYPbk3JQRLpKN20Jecko&fields=name,rating,formatted_phone_number&key=MyKey`;
const {data} = await axios.get(url)
console.log(data)
} catch (error) {
console.log(error.message)
}
}
and
export const fetch_information = (skip, limit, filter) => async (dispatch) => {
try {
var url = `https://maps.googleapis.com/maps/api/place/details/json?`;
let config = {
params: {
place_id: 'ChIJk0aJYPbk3JQRLpKN20Jecko',
key: 'myKey',
},
}
const {data} = await axios.get(url, config)
console.log(data)
} catch (error) {
console.log(error.message)
}
}
I think that the request looks a bit messy. I'm under the impression that you are trying to pass results to a redux store. Let's see if we can clean this up a bit.
export const fetch_information = async () => dispatch => {
const req = await axios.get("https://maps.googleapis.com/maps/api/place/details/json?place_id=ChIJk0aJYPbk3JQRLpKN20Jecko&fields=name,rating,formatted_phone_number&key=MyKey");
const data = await req.json();
return data;
//or, for your purpose...
console.log(data);
//can also dispatch for store
}
I didn't see anything you were passing as necessary for this.
My question is about correctly implementing an async function to fetch data. I've a function called _getData() and I'm calling it on the componentDidMount() of a screen. But when server response is slow, switching to this screen is getting slower. So I would like to use async function for fetching data. But I'm not sure if I'm doing it correctly. Is that a correct approach? I can't be sure if it works async or not.
Here is my Api._getData() code:
const _getData = async () => {
return await axios.get("http://blabla.com/someservice", { params: someParamDataHere });
};
export const Api = {
_getData
};
and on SomeScreen.js, I also have loadData() function which calls the function above and does state updates.
loadData() {
Api._getData()
.then((response) => {
this.setState({ myData: response.data });
})
.catch((error) => {
console.log(error.response);
});
}
in componentDidMount() function of the same screen I'm calling this loadData() function directly.
Now, is it enough to declare Api._getData() as async and using await in it, or should I change some trigger functions too?
Thank you very much for your help.
instead of async await use promises
export const getRequest = (url) => {
return new Promise((resolve, reject) => {
api
.get(url)
.then((response) => {
handleReponse(response)
.then((errorFreeResponse) => {
resolve(errorFreeResponse);
})
.catch((error) => {
reject(error);
});
})
.catch((error) => {
reject(handleError(error));
});
});
};
You are doing correct while retrieving in load Data . What you can do more is try more syntactical sugar of es6 by using async await in loadData , hence
loadData = async() =>{
try{
let response = await Api._getData();
this.setState({ myData: response.data });
} catch(err){
console.log(error.response);
}
}
Hope it helps. feel free for doubts