Dispatching action from onUploadProgress event using Redux-Thunk / Axios - reactjs

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

Related

Wait for get request response before continuing with a post request in axios

I have tried to use async/await in the createSwap function but it did not work for me. Before creating a request, I want to run the getSlotDetails function to set the slotDets so that it can be used in the post request below. However, the createSwap function does not wait for the getSlotDetails function to complete before sending the post request. How do I fix this?
const [slotDets, setSlotDets] = useState([]);
const getSlotDetails = (moduleCode, slotId, slotType) => {
axios
.get(`https://api.nusmods.com/v2/2020-2021/modules/${moduleCode}.json`)
.then((response) => response.data)
.then((data) => data.semesterData[0])
.then((semesterData) => semesterData.timetable)
.then((timetable) =>
timetable.filter(
(slot) => slot.classNo === slotId && slot.lessonType == slotType
)
)
.then((result) => {
setSlotDets(result);
});
};
const createSwap = (
moduleCode,
slotType,
currentSlot,
desiredSlots,
completed,
reserved
) => {
dispatch(createRequest());
getSlotDetails(moduleCode, currentSlot, slotType);
axios
.post(
"http://localhost:3001/api/v1/swaps",
{
module_code: moduleCode,
slot_type: slotType,
current_slot: currentSlot,
desired_slots: desiredSlots,
completed: completed,
reserved: reserved,
venue: slotDets.venue,
startTime: slotDets.startTime,
endTime: slotDets.endTime,
day: slotDets.day,
},
{
headers,
}
)
.then((response) => {
console.log(response.data);
dispatch(createSuccess());
setTimeout(() => {
dispatch(resetSwap());
}, 2000);
})
.catch((error) => {
console.log(error.response.data);
dispatch(createFail(error.response.data));
setTimeout(() => {
dispatch(resetSwap());
}, 2000);
});
};
const makeTwoRequests = async () => {
try{
const firstResponse = await axios.get(FIRST_URL);
if(firstResponse.status === 200){
const firstResponseData = firstResponse.data;
const secondResponse = await axios.get(SECOND_URL);
if(secondResponse.status === 200){
const secondResponseData = secondResponse.data;
}
}
}
catch(error){
console.log(error)
}
}
maketwoRequests()
firstResponseData and secondResponseData can be state variables instead of const for better access throughout the Component

Dispatch multiples http request React/Redux

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

Cancelling Axios get request in React

I have an Axios get request I'd like to cancel upon an event but it doesn't seem to work.
// user.services.js
searchFAQS(query) {
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
source.cancel('Operation cancelled by user')
return axios.get(
authHeader.getApiUrl() + '/api/v1/search_faqs',
{
cancelToken: source.token,
params: {
query: query
}
}
)
}
// ClassComponent
onChangeQuery = (e) => {
if (e.target.value === "") {
this.setState({
fFaq: "",
query: e.target.value
})
} else {
UserServices.searchFAQS().cancel()
this.setState({query: e.target.value},
() => UserServices.searchFAQS(this.state.query)
.then(resp => {
this.setState({
fFaq: resp.data,
fGroups: resp.data.map(f => f.group).filter((value, index, self) => self.indexOf(value) === index)
})
}))
}
}
I read the cancellation part for the Axios documentation, which is what led me to the attempt above, but it doesn't seem to be canceling after observing the requests from developer tools.
searchFAQS(query) {
const CancelToken = axios.CancelToken;
.....
new CancelToken is creating on every searchFAQS call, so it will not get cancel because everytime it's a new token
change as below
let token = null; // define cancel token outside the search fn, then it will not recreate on every call
searchFAQS(query) {
if (token !== null) {
token();
}
...
const { CancelToken } = axios;
...
return axios.get(
authHeader.getApiUrl() + '/api/v1/search_faqs',
{
cancelToken: new CancelToken(function executor(cancellableFn) {
token = cancellableFn;
}),
params: {
query: query
}
}
....
On my understanding you solution should looks like this:
// user.services.js
async searchFAQS(query, source = '') {
const search = axios.get(
authHeader.getApiUrl() + '/api/v1/search_faqs',
{
cancelToken: source.token,
params: {
query: query
}
}
);
if (source /* change to your needs, actualy it cancels all requests */) {
source.cancel('Ok, its just canceled!');
}
return await search.data;
}
// ClassComponent
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
onChangeQuery = (e) => {
if (e.target.value === "") {
this.setState({
fFaq: "",
query: e.target.value
})
} else {
UserServices.searchFAQS("", source)
this.setState({query: e.target.value},
() => UserServices.searchFAQS(this.state.query, source)
.then(resp => {
this.setState({
fFaq: resp.data,
fGroups: resp.data.map(f => f.group).filter((value, index, self) => self.indexOf(value) === index)
})
}))
}
}

I'm getting following error in Jest ReferenceError: Response is not defined

First I had a typescript issue that my mocked data doesn't match to Response type. Then I tried to create mock data with Response constructor and got that error.
I have the following code
const HttpRequest = async function (
url: string,
options: RequestInit,
): Promise<Response> {
try {
return await fetch(url, options);
} catch (error) {
return null;
}
};
export default HttpRequest;
And following test:
const res = new Response();
window.fetch = jest.fn(
() => new Promise((resolve) => {
return resolve(res);
})
);
describe('HttpService', () => {
it('fetchWithFeedback', async () => {
const data = await HttpRequest('/api', { method: 'GET' });
expect(data).toEqual(res);
});
});
Change
const changeField = (field, id, value) => {
const newPropertyData = { ...propertyData };
if (newPropertyData.id === id) {
newPropertyData.field = value;
}
};
to
const changeField = (field, id, value) => {
if (propertyData.id === id) {
setPropertyData({ ...propertyData, [field]: value })
}
};
You're not updating your state in your onChange, so it's never able to update the value, which is still "".

React native - React Hook useEffect has a missing dependency:'getAllPost'. Either include it or remove the dependency array.",

I am new in react native and try to call two api from useEffect but it give me this error every time React Hook useEffect has a missing dependency: 'getAllPost'. Either include it or remove the dependency array.
Here is my code
export default function Home({navigation}) {
const [arrCat, setArrCat] = useState([]);
const [arrPost, setArrPost] = useState([]);
const [isLoading, setLoding] = useState(false);
function getAllCategory() {
setLoding(true);
let apiResponse = ApiManager.GET('category/all', [], 'GET');
apiResponse
.then(response => {
let responseJson = response[1];
let status = response[0];
setLoding(false);
let message =
responseJson.message != null
? response.message
: 'Something went wrong';
if (status === 200) {
setArrCat([...responseJson.data]);
getAllPost();
}
setTimeout(function() {
if (message != null) {
Toast.showWithGravity(message, Toast.LONG, Toast.BOTTOM);
}
}, 120);
})
.catch(error => {
console.error(error);
Toast.showWithGravity(error, Toast.LONG, Toast.BOTTOM);
setTimeout(function() {
setLoding(false);
}, 60);
});
}
function getAllPost() {
GetLocation.getCurrentPosition({
enableHighAccuracy: true,
timeout: 15000,
})
.then(location => {
console.log(location);
const dictData = {
lat: '-37.81400200-33.865143', //location.latitude,
lang: '144.9546943', //location.longitude,
record_count: '0',
};
console.log(dictData);
let apiResponse = ApiManager.POST(
'post/getRecommendedPost',
dictData,
'POST',
);
apiResponse
.then(response => {
let responseJson = response[1];
let status = response[0];
if (status === 200) {
console.log(responseJson);
setArrPost(oldValue => [...oldValue, ...responseJson.data]);
console.log(arrPost);
} else {
// console.error(responseJson);
Toast.showWithGravity(
responseJson.message,
Toast.LONG,
Toast.BOTTOM,
);
}
})
.catch(error => {
// console.error(error);
Toast.showWithGravity(error.message, Toast.LONG, Toast.BOTTOM);
// setTimeout(function() {
// setLoding(false);
// }, 60);
});
})
.catch(error => {
// const {code, message} = error;
// console.warn(code, message);
Toast.showWithGravity(error.message, Toast.LONG, Toast.BOTTOM);
});
}
useEffect(() => {
console.log('Home screen mounted');
getAllCategory();
// getAllPost();
}, []);
return ( ....)
}

Resources