I'm trying to dispatch more than one axios request inside my method. However, it is not working.
export const getImages = (res) => {
return {
type: actionTypes.GET_IMAGES,
payload: res
}
}
export const loadImages = (imgs, cId) => {
return dispatch => {
let data = [];
for(const i of imgs) {
const id = i.id;
axios.get(`${api.URL}/test/${cId}/files/${id}`)
.then(res => {
if(res.data !== -1) {
const obj = {
name: res.data,
desc: i.caption
};
data(obj);
}
//dispatch(getImages(data));
});
}
console.log('Action:');
console.log(data);
dispatch(getImages(data));
}
}
The console log does not print anything. Do I need to dispatch inside the .then()? If so, how can I run multiples requests before dispatching?
Thanks
Related
I change the redux in my project to ReactQuery,and i got some problem with put req in my code.
this is my code
const { dispatch } = store;
const editClientDataAsync = async ( id,data ) => {
await axiosObj().put(`clients/${id}`,data);
}
const { mutateAsync: editClientData, isLoading } = useMutation(['editClientData'], editClientDataAsync, {
onSuccess: () => dispatch({ type: SUCCESS_DATA, payload: { message: "Success" } }),
onError: () => dispatch({ type: ERROR_DATA, payload: { message: "Error" } })
});
return { editClientData, isLoading }
}
same problem with when i try to get some req with id
const id = useSelector((state) => state?.clientData?.clientInfo?.data.id)
const getClientDetails = async ({ queryKey }) => {
const [_, { id }] = queryKey;
console.log(queryKey)
if (!id)
return;
const { data } = await axiosObj().get(`clients/${id}`)
console.log(data)
return data;
}
const { data: clientDetails, isLoading } = useQuery(['ClientId', { id }], getClientDetails)
return { clientDetails, isLoading }
Mutation functions only take 1 argument
Check where you use the editClientData mutation and pass the arguments in one object.
const editClientDataAsync = async ({ id, data }) => {
await axiosObj().put(`clients/${id}`,data);
}
return useMutation(['editClientData'], editClientDataAsync, ...);
Are you sure you get an id passed to the function?
You can disable the query until you get that id with the enabled option, so you don't make an unnecessary http call.
const id = useSelector((state) => state?.clientData?.clientInfo?.data.id)
const getClientDetails = async (id) => {
const { data } = await axiosObj().get(`clients/${id}`)
return data;
}
return useQuery(['client', id], () => getClientDetails(id), { enabled: !!id })
Disable/pausing queries
nestjs controller.ts
#Patch(':id')
async updateProduct(
#Param('id') addrId: string,
#Body('billingAddr') addrBilling: boolean,
#Body('shippingAddr') addrShipping: boolean,
) {
await this.addrService.updateProduct(addrId, addrBilling, addrShipping);
return null;
}
nestjs service.ts
async updateProduct(
addressId: string,
addrBilling: boolean,
addrShipping: boolean,
) {
const updatedProduct = await this.findAddress(addressId);
if (addrBilling) {
updatedProduct.billingAddr = addrBilling;
}
if (addrShipping) {
updatedProduct.shippingAddr = addrShipping;
}
updatedProduct.save();
}
there is no problem here. I can patch in localhost:8000/address/addressid in postman and change billingAddr to true or false.the backend is working properly.
how can i call react with axios?
page.js
const ChangeBillingAddress = async (param,param2) => {
try {
await authService.setBilling(param,param2).then(
() => {
window.location.reload();
},
(error) => {
console.log(error);
}
);
}
catch (err) {
console.log(err);
}
}
return....
<Button size='sm' variant={data.billingAddr === true ? ("outline-secondary") : ("info")} onClick={() => ChangeBillingAddress (data._id,data.billingAddr)}>
auth.service.js
const setBilling = async (param,param2) => {
let adressid = `${param}`;
const url = `http://localhost:8001/address/`+ adressid ;
return axios.patch(url,param, param2).then((response) => {
if (response.data.token) {
localStorage.setItem("user", JSON.stringify(response.data));
}
return response.data;
})
}
I have to make sure the parameters are the billlingddress field and change it to true.
I can't make any changes when react button click
Since patch method is working fine in postman, and server is also working fine, here's a tip for frontend debugging
Hard code url id and replace param with hard coded values too:
const setBilling = async (param,param2) => {
// let adressid = `${param}`;
const url = `http://localhost:8001/address/123`; // hard code a addressid
return axios.patch(url,param, param2).then((response) => { // hard code params too
console.log(response); // see console result
if (response.data.token) {
// localStorage.setItem("user", JSON.stringify(response.data));
}
// return response.data;
})
}
now it worked correctly
#Patch('/:id')
async updateProduct(
#Param('id') addrId: string,
#Body('billingAddr') addrBilling: boolean,
) {
await this.addrService.updateProduct(addrId, addrBilling);
return null;
}
const ChangeBillingAddress = async (param) => {
try {
await authService.setBilling(param,true).then(
() => {
window.location.reload();
},
(error) => {
console.log(error);
}
);
}
catch (err) {
console.log(err);
}
}
const setBilling= async (param,param2) => {
let id = `${param}`;
const url = `http://localhost:8001/address/`+ id;
return axios.patch(url,{billingAddr: param2}).then((response) => {
if (response.data.token) {
localStorage.setItem("user", JSON.stringify(response.data));
}
return response.data;
})
}
So I have these two functions - One is an upload function (uploads file to DB). And the other function needs the url's of the uploaded files and for some reason I'm not able to wait for the upload function to finish so I can get the url's of the images uploaded so I can store them in my database.
the problem is at
uploadedImages.map(async file => {
urls.push(await uploadedFiles(file, uid, key))
// console.log(urls)
})
In the following code. Line 12.
export const handleUploads = (uploadedImages, uid, key, directory) => {
return (dispatch, getState, {
getFirebase,
getFirestore
}) => {
const firestore = getFirestore();
//const uid = getState().firebase.auth.uid;
let urls = []
uploadedImages.map(async file => {
urls.push(await uploadedFiles(file, uid, key))
// console.log(urls)
})
return firestore.collection('Uploads').doc(key).set({
uid: uid,
urls: urls,
timestamp: new Date(),
directory: directory
}).then(() => {
dispatch({
type: 'UPLOAD_SUCCESS'
});
toastr.success('הודעה', 'עודכן בהצלחה')
console.log(key)
}).catch(err => {
dispatch({
type: 'UPLOAD_ERROR'
}, err);
toastr.error('אופס! אירעה שגיאה')
});
}
};
Heres the upload function itself:
export const uploadedFiles = (file, uid, key) => {
// const firestore = getFirestore();
// const key = firestore.ref().child(property.uid).push().key
// const img = storage.ref().child(property.uid).child(key)
// const uploadedImages = property.uploadedImages
//let uploaded_arr = []
const uploadTask = storage.ref(`Properties/${uid}/${key}/${file.name}`).put(file);
uploadTask.on('state_changed',
(snapshot) => {
// progrss function ....
// const progress = Math.round((snapshot.bytesTransferred / snapshot.totalBytes) * 100);
//
// console.log(progress);
},
(error) => {
// //error function ....
// dispatch({ type: 'UPLOAD_ERROR'}, error);
toastr.error('הודעת מערכת', error)
return ''
},
() => {
// //complete function ....
storage.ref('Properties').child(String(uid)).child(String(key)).child(file.name).getDownloadURL().then(url => {
// console.log(url)
return url
})
});
}
A solution would be highly appreciated.
P.S there is no error, its just that the await doesn't await the function to execute.
It simply has no effect.
uploadedFiles should return a promise for your await to work. Await makes javascript to wait until the promise returns with a result. Now your uploadedFiles below resolves url if it successfully gets updated in the database else, it gives out empty string.
Hence, update your uploadedFiles like so...
export const uploadedFiles = (file, uid, key) => {
return new Promise(function(resolve, reject) {
// const firestore = getFirestore();
// const key = firestore.ref().child(property.uid).push().key
// const img = storage.ref().child(property.uid).child(key)
// const uploadedImages = property.uploadedImages
//let uploaded_arr = []
const uploadTask = storage.ref(`Properties/${uid}/${key}/${file.name}`).put(file);
uploadTask.on('state_changed',
(snapshot) => {
// progrss function ....
// const progress = Math.round((snapshot.bytesTransferred / snapshot.totalBytes) * 100);
//
// console.log(progress);
},
(error) => {
// //error function ....
// dispatch({ type: 'UPLOAD_ERROR'}, error);
toastr.error('הודעת מערכת', error)
reject()
},
() => {
// //complete function ....
storage.ref('Properties').child(String(uid)).child(String(key)).child(file.name).getDownloadURL().then(url => {
// console.log(url)
resolve(url);
})
});
});
}
This is because map will execute all the async lambdas immediately after each other. If you have n element in that array, then map executes n async functions without waiting for the promises being finished.
You can do that using Promise.all. Here the example.
export const handleUploads = (uploadedImages, uid, key, directory) => {
return async (dispatch, getState, { getFirebase, getFirestone }) => {
// ...
const urls = await Promise.all(
uploadedImages.map(async file => uploadedFiles(file, uid, key))
);
// ...
return ...
}
}
Where uploadedFiles needs to be async, too.
I'm using Redux with redux-thunk to retrieve categories from an API. I have an action called viewCategory that depends on having categories in the store state.
I used the example of fetching Reddit posts from the Redux site:
https://redux.js.org/advanced/asyncactions#actions-js-asynchronous
The problem I have is that when I call viewCategory the promise thinks it's resolved when REQUEST_CATEGORIES is dispatched and not RECEIVE_CATEGORIES. So if log my state in the then statement I have an
empty list of categories.
export function viewCategory(urlKey) {
return (dispatch, getState) => {
dispatch(fetchCategoriesIfNeeded()).then(() => {
let state = getState();
console.log(state); // should have categories
let categories = [...state.categories.mainCategories,
...state.categories.specialCategories];
let matchCategory = categories.find((category) => {
return category.custom_attributes.find(x => x.attribute_code === "url_key").value === urlKey;
});
dispatch({
type: Categories.VIEW_CATEGORY,
category: matchCategory
});
});
};
}
The functions that decide if categories should be fetched at all:
function shouldFetchCategories(state) {
const categories = state.categories;
if(categories.isFetching || categories.mainCategories.length > 0) {
return false;
} else {
return true;
}
}
export function fetchCategoriesIfNeeded() {
return (dispatch, getState) => {
if(shouldFetchCategories(getState())) {
return dispatch(fetchCategories());
} else {
return Promise.resolve();
}
};
}
The function that contains the actual fetch call:
function fetchCategories() {
return (dispatch, getState) => {
dispatch(requestCategories());
const {locale} = getState().settings;
return fetch(`${BASE_URL}/categories/list`, {
method: "POST",
headers: {
"Accept-Language": locale
},
body: "Not interesting for stackoverflow"
})
.then(response => response.json())
.then(json => {
if(json !== undefined && json.items){
dispatch(receiveCategories(json.items));
}
});
};
}
The functions where I dispatch types REQUEST_CATEGORIES & RECEIVE_CATEGORIES:
function requestCategories() {
return {
type: Categories.REQUEST_CATEGORIES
};
}
function receiveCategories(result) {
const mainCategories = result.filter(category => category.level === 2);
const subCategories = result.filter(category => category.level === 3);
const categories = mainCategories.map(category => {
let children = subCategories.filter(x => x.parent_id === category.id);
return {
...category,
children
};
});
let specialCategories = categories.splice(categories.length - 2, 2);
return {
type: Categories.RECEIVE_CATEGORIES,
categories: categories,
specialCategories: specialCategories,
receivedAt: Date.now()
};
}
Any idea what I am doing wrong here? If you need any extra code or information please let me know.
The following code uploads a file no problem and responds successfully or failing as expected, however, I cannot figure out how to dispatch my uploadFileProgress action from the onUploadProgress event. I can console.log the progress / percentage and when I try to wrap the dispatch in an IIFE, I trigger a dispatch is not a function error. Hopefully this is a small issue I'm missing. Thanks in advance!
export function uploadFile(values, callback = () => {}) {
const uploadFileData = new FormData();
uploadFileData.append('fileName', values.fileName);
uploadFileData.append('file', values.file);
uploadFileData.append('file', {
filename: values.filename,
contentType: values.contentType,
});
const uploadProgress = {
onUploadProgress: (ProgressEvent) => {
let progressData = 0;
const totalLength = ProgressEvent.lengthComputable ? ProgressEvent.total : ProgressEvent.target.getResponseHeader('content-length') || ProgressEvent.target.getResponseHeader('x-decompressed-content-length');
if (totalLength !== null) {
progressData = Math.round((ProgressEvent.loaded * 100) / totalLength);
}
return function action(dispatch) {
dispatch(uploadFileUpload(progressData));
};
},
};
const configPlusProgress = Object.assign(uploadProgress, config);
const request = () => axios.post(myURL, uploadFileData, configPlusProgress);
return function action(dispatch) {
dispatch(uploadFileLoading(true));
return request()
.then((response) => {
if (response.status !== 201) {
dispatch(uploadFileFail());
throw Error(response.statusText);
}
dispatch(uploadFileLoading(false));
return response;
})
.then(response => dispatch(uploadFileSuccess(response)))
.then(() => callback())
.catch(err => dispatch(uploadFileFail(err)));
};
}
move your request config inside returned function (where dispatch function will be accessible):
export function uploadFile(values, callback = () => {}) {
const uploadFileData = new FormData();
uploadFileData.append('fileName', values.fileName);
uploadFileData.append('file', values.file);
uploadFileData.append('file', {
filename: values.filename,
contentType: values.contentType,
});
return function action(dispatch) {
const uploadProgress = {
onUploadProgress: (ProgressEvent) => {
let progressData = 0;
const totalLength = ProgressEvent.lengthComputable ? ProgressEvent.total : ProgressEvent.target.getResponseHeader('content-length') || ProgressEvent.target.getResponseHeader('x-decompressed-content-length');
if (totalLength !== null) {
progressData = Math.round((ProgressEvent.loaded * 100) / totalLength);
}
dispatch(uploadFileUpload(progressData));
},
};
const configPlusProgress = Object.assign(uploadProgress, config);
const request = () => axios.post(myURL, uploadFileData, configPlusProgress);
dispatch(uploadFileLoading(true));
return request()
.then((response) => {
if (response.status !== 201) {
dispatch(uploadFileFail());
throw Error(response.statusText);
}
dispatch(uploadFileLoading(false));
return response;
})
.then(response => dispatch(uploadFileSuccess(response)))
.then(() => callback())
.catch(err => dispatch(uploadFileFail(err)));
};
}
Also onUploadProgress should just dipatch upload progress event.
I can't quite fix your code but here is a basic function with redux-thunk doing async stuff and using actions.
const doSomeAsyncStuff = () =>
async ( dispatch ) => {
try {
const response = await someAsyncStuff();
return dispatch( someSuccessAction( response.data );
} catch ( error ) {
return dispatch( someFailureAction( err );
}
}
Of course redux-thunk must be added as a middleware.
why are you returning a function from onUploadProgress function
return function action(dispatch) {
dispatch(uploadFileUpload(progressData));
};
Instead of that you can just
dispatch(uploadFileUpload(progressData));