ReactJS Redux RxJS AJAX loading indicator - ajax

I have a ReactJS & Redux application, and using RxJS for async AJAX with cancellation and error handling. The middleware (for Redux) I am using to connect Redux and RxJS is redux-observable.
I am having a request to the server which also handles errors and cancellation, this is the epic:
const RecipesEpic = action$ =>
action$.ofType(FETCH_RECIPES)
.mergeMap(action => {
return Observable.ajax({
method: "get",
url: url
})
.map(payload => {
return ({ type: FETCH_RECIPES_FULFILLED, payload })
})
.catch(error => Observable.of({
type: FETCH_RECIPES_REJECTED,
payload: error
}))
.takeUntil(action$.ofType(FETCH_RECIPES_CANCELLED))
})
export default RecipesEpic
I'm trying to implement a loading indicator, first I want to log to the console how many percents completed of the request (by using XHR2), and then I will implement the UI. In simple words: How to implement XHR2 download progress in RxJS DOM Request?
I didn't find any resources online about this question, and I hope other people who are searching for this question will have an answer.
Thank you!

According to The RxJS Ajax Documentation you can supply a progressObserver in your Observable.ajax call. The Observer you supply will be updated with the XHR2 download progress!

Related

How do I log a specific field in API requests within Cypress

I want to cypress.log() out a specific field in the request header whenever my webapp makes requests that way when it fails and adds screenshots/logs I can grab that that requestId that failed.
Is there a way to setup cypress so that for all network requests it checks for this field and log it?
I can add a cy.intercept within each individual file but I want a more generic way to handle this.
Cypress.log is the synchronous version of cy.log().
Add middleware: true to the intercept to pass request to other intercepts.
cy.intercept({ url: '*', middleware: true }, (req) => {
const headerValue = req.headers?['x-custom-headers']
if (headerValue) {
Cypress.log({
name: 'x-custom-header',
message: headerValue
})
}
})
You'll get an Cypress promise error if you try to use cy.log() to log out every request header in an cy.intercept() within a routeHandler callback. This would also make it kind of tough to log to a CI terminal as well.
Instead you can console.log to dev tools. To make it apply to all tests, you can wrap it in a beforeEach() and place it in the support/index.js file.
// support/index.js
beforeEach(() => {
cy.intercept('*', (req) => {
req.continue((res) => {
console.log(JSON.stringify(req.headers))
})
})
})

What is the difference between catch and catchError in RxJS. And How to handle the network error returned by API call?

mergeMap( (action)=>{
const data = action.data;
console.log(state$.value,'\n',action.data);
Calling API here. How to handle the network error returned by this call?
from(axios.post('http://localhost:3000/addContactIntoDirectory',
{directoryId: state$.value.reducer1.SelectedDirectory, contact: data.contact})
))
basically, in RXJS catch and catchError is identical. You can refer the documentation RxJs catch/catchError for more info. Docs also states that we have to return observable from catchError.
have a look at the given example related to your library axios context,
axios.post('/formulas/create', { name: "Atul", parts: "Mishra" })
.then(response => {
console.log(response)
}).catch(error => {
console.log(error.response)
});

How to get response headers from RxJS's ajax?

I am creating new frontend for an interview system. Some its API endpoints is updated, so getting pagination info is not a problem, but old ones still have pagination data inside response headers.
P.S. we are using react, redux and redux-observable
RxJS has the following call:
ajax({ ...params }).pipe(
map(response => {
// here I need to somehow get headers from ajax response
}),
catchError(errorResponse => {
// return error
})
)
I've been looking for the same answer, looks like there is a way (See: https://stackblitz.com/edit/typescript-k2ggm2?file=index.ts):
ajax({ ...params }).pipe(
map(response => {
// here I need to somehow get headers from ajax response
console.log(response.xhr.getAllResponseHeaders())
console.log(response.xhr.getResponseHeader('pragma'))
}),
catchError(errorResponse => {
// return error
})
)

Axios POST requests taking too long and being duplicated at WebApi

Today I've received bug reports that some of our application's POST requests are being duplicated.
These requests results in the creation of objects on the remote database such like Tasks, Meetings, etc, so the duplication of them implies in Tasks being created with the same name, same due date, user, etc.
I've tried to replicate this but it seems to be a random behavior, and the only concurrent symptom reported is that the request takes more time than normal to complete.
I'm currently using the stack React + Redux + Axios + Asp.Net WebApi, and I have reached to the following considerations in order to understand and solve the issue.
Any tips on any of these topics are really appreciated.
Root cause identification
React + Redux:
The action creator that dispatches the request is called just once, on a onClick event. Apparently there's no state change or page refresh that could cause a multiple call of this fuction.
handleClick(event) {
var postData = {
title: this.state.text,
dueDate: this.state.date,
assignedUser: this.state.user.id
};
this.props.contentPost(postData);
}
Axios:
One of my suspicions is that, for some unknown reason, the users' requests fails, or take to long to complete and then axios supposedly sends the request it again. I've checked it's documentation and issues databases, and didn't found nothing like Automatic Retries or request duplication.
export const contentPost = (props) => {
return function (dispatch) {
dispatch({ type: CONTENT_POST_ERROR, payload: null })
dispatch({ type: CONTENT_POST_LOADING, payload: true });
dispatch({ type: CONTENT_POST_SUCCESS, payload: null })
const request = axios({
method: 'post',
url: `/api/content`,
headers: auth.getHttpHeader(),
validateStatus: function (status) {
return status >= 200 && status < 300; // default
},
data: props
}).then((res) => {
dispatch({ type: CONTENT_POST_LOADING, payload: false });
dispatch({ type: CONTENT_POST_SUCCESS, payload: true, data: res })
}).catch(err => {
dispatch({ type: CONTENT_POST_ERROR, payload: err.code })
dispatch({ type: CONTENT_POST_LOADING, payload: false });
dispatch({ type: CONTENT_POST_SUCCESS, payload: false, data: null })
});
}
};
WebApi
The Controller's method don't have any sort of throttling or "uniqueness token" to identify and prevent duplicated requests. Since this is a random behavior, I would not bet that the Routing or any part of my server side application has any responsibility on this.
Solutions so far...
Axios:
Throttle the axios request at my action creator, to prevent it to be called to often at a given time interval.
Send a client-generated "uniqueness token" in the request body
WebApi
Implement request filtering/throttling, in order to prevent duplicated request, based on body contents at a given time interval.
Receive and handle the "uniqueness token" to prevent post duplicity.
Even with the described above, these solutions looks like an overkill, while the root causes also seems a little too subjective.
I've found out that a late loading state feedback for the users was causing this behavior.
Some people stills double-click stuff on websites. It was naive not to have prevented this by code.
There are a lot of ways to accomplish a better user experience in this scenario. I chose to completely change the button while user are sending our POST request, and we are also implementing the throttling at our backend.
{this.props.content_post_loading &&
<a className="btn btn-primary disabled">Creating...</a>
||
<a className="btn btn-primary" onClick={this.handleCreateClick}>Create</a>
}

How do I load inital set of data with ajax call in React redux?

I have a problem with redux trying to load initial data with an asynchronous call to my backend API that returns a JSON. Right now, I'm trying to load a bunch of different articles, but I have no idea how to load it asynchronously. Since it is an initial set of data, should I load it synchronously? If yes, then how would I acheive a synchronous call to my API? If not, how would I go about solving this problem asynchronously?
Right now, I have static json data, data/articles.js that creates a default state in store.js.
Thanks
You should use a redux-thunk middleware, which allows you to dispatch async actions and a fetch library (for example) for downloading your initial data.
So:
1) create an action which fetch your data, example:
export function fetchData() {
const options = {
method: 'GET',
headers: {
'Authorization': 'Client-ID xx' // if theres any needed
}
}
return (dispatch) => {
return fetch('yourUrl.json', options)
.then(response => response.json())
.then(data => dispatch(receiveYourData(data)))
.catch(err => console.log(err));
}
}
receiveYourData is a action which will place your data in your state, example:
export function receiveYourData (payload = []) {
return {
type: RECEIVE_DATA,
payload: payload
}
}
Of course you have to write action handler, which after dispatching an action, will place your data in your state.
If you have your setup (similar to above), you should dispatch fetchData in your componentDidMount lifecycle method (its one of the option of course :) ).
If you dont know how to do particular parts, you can refer to this Example.
Also official async example may be helpful :)
I also had this problem. It turned out that you have to add a lot of code for this simple task. So I simplified this process and created a package for async loading of initial state in redux - redux-async-initial-state.
You can check out examples and in your case in the end your store creator will look like this:
// Load initial state function
const loadStore = () => {
return Promise(resolve => {
fetch('/data/articles.js')
.then(response => response.json())
.then(resolve);
});
}
const storeCreator = applyMiddleware(asyncInitialState.middleware(loadStore));
const store = storeCreator(reducer);

Resources