Where to make Ajax calls in React flux - javascript

I have a react js app where the user can create a user and then i make a http post to the backend.
i have a action that looks like this
export function createUser(name, username, password) {
dispatcher.dispatch({
type: "CREATE_USER",
name,
username,
password,
});
}
then in my store i call action and it triggers a function that makes a http post to the backend that looks like this
handleActions(action) {
switch(action.type) {
case "CREATE_USER": {
this.createUser(action.name, action.username, action.password);
break;
}
default:
}
}
should i make the ajax call in the store or in the action it self?

First of all you would want redux-thunk which give you opportunity to create actions which dispatches other actions in async way.
After this you can create an action which make a call to server, and on result dispatch new action which would bring to store new data. For example:
getData(param) {
return (dispatch) => {
dispatch(dataRequestAction());
return fetch(`/data/${param}`)
.then(r => r.json())
.then(data => dispatch(setDataAction(data)))
.catch(err => dispatch(errroDuringRataRetrieving(err)))
};
}
as you see here you have one action (getData) which actually doesn't change the store, but trigger 'dataRequestAction' which put in to store data that request started. Then if request completed one of action can be triggered:
setDataAction - if all ok;
errroDuringRataRetrieving - if request failed.
In this way you can handle ajax via redux.

I think we should have seperate folder called api. There we will have our all api calls. We can inject those file and call those function where we put our functions which call api and respond action.

Related

Angular, how to make consecutive, dependent http get requests

I'd like to execute one http get request after another has completed. The endpoint URL for the second request will need to depend on the first request.
I've tried nesting the requests with something like this in Angular:
this.http.get(endpoint1).subscribe(
success => {
this.http.get(endpoint2 + success).subscribe(
anotherSuccess => console.log('hello stackoverflow!')
);
}
);
First question here on stackoverflow, please let me know if I should provide more detail.
here you can find how to do that, you have various options:
with subscribe:
this.http.get('/api/people/1').subscribe(character => {
this.http.get(character.homeworld).subscribe(homeworld => {
character.homeworld = homeworld;
this.loadedCharacter = character;
});
});
with mergeMap
this.homeworld = this.http
.get('/api/people/1')
.pipe(mergeMap(character => this.http.get(character.homeworld)));
There is a version with forkJoin but isn't explicit compared with subscribe and mergeMap.
You should probably try using ngrx (redux) triggering the second request depending on a success action.
The flow should be something like this: dispatch an action from a component -> call the first request -> the request triggers a success action -> success effect triggers the second request with a payload from the previous request.
Read the docs here

Redirect/ Routing issues in React and Express

I am creating a flight search app that makes external api calls. The data will be reorganized and returned in search results component.
On submit, the data is sent to express and takes about 10 seconds or more to complete all the api calls.
I think I need a loader at some point for during the delay of api calls, but also I am unsure of how to send/render the data.
As it stands, I have two pages home.js- '/' where i make the search and is sent to the server side, and prices.js- '/search' which when loaded fetches the data from the json file. but i do not have them connected
Both files work but I need to connect them. When I press submit, the user inputs are sent to server and the api calls are made but in order to see the results i have to manually refresh localhost:3000/search.
In express app after all the api calls, I tried res.redirect method, however the error given was setting the headers after sent to the client.
In react, I tried after submitting, to redirect to the search page. However I could not get it to redirect and also as soon as the /search page is called, it fetches the data from the file. This will happen before the api has finished writing to file and therefore the previous search results will render.
--in app.js
setTimeout(() => {
Promise.all([promise, promise2]).then(values => {
return res.redirect('http://localhost:3000/search');
});
}, 25000);
I had to wrap the api calls in promises so it will only redirect after all is written to file.
(in react prices.js)
componentDidMount() {
fetch('/search')
.then(res => {
return res.json()
})
.then(res => {
console.log(res.toString());
this.setState({flightData: res});
})
.catch(error => console.log(error));
}
home.js
home.js
```
onChange = (e) => {
this.setState({
originOne: e.target.value, originTwo: e.target.value});
};
onSubmit = (e) => {
e.preventDefault();
const { originOne, originTwo ,redirectToResult} = this.state;
};
```
app.js - I have all the functions calling each other in a waterfall style ( probably not the best way I know)
app.post('/', function getOrigins(req,res) {
var OrigOne;
var OrigTwo;
....
function IataCodeSearchOrg1(res, OrigOne, OrigTwo) {
...
findPrices(res,A,B)
}
function findPrices(res, A, B) {
promise = new Promise(function (resolve) {
...
}
}
All the methods are called within eachother. The api calls are in a loop and after each iteration they are written to the json file.
All these functions are in the app.post method and i tried to res.redirect but it did not work.
EDIT:
You can't redirect server-side from an XHR request. You would need to redirect client-side.
e.g.
componentDidMount() {
fetch('/search')
.then(res => res.json())
...
.then(() => window.location = '/myredirecturl')
}

React/Redux chaining a second API request and dispatch

Still getting used to this.
I have checked out questions here and here and I still feel uncomfortable about how to go about this. These are not clear from my perspective.
Basically I have clicked on the "Login" button and have requested a JWT login token and succesfully received it. What I have noticed is that I can at this point download those rarely changed lists like states or suburbs/postcodes which are large (the latter) and should be at the spa's immediate use rather than tacking it onto a call for a client.
I want to, upon successfully receiving the token immediately do an API call for those lists and store them in local storage. There is no "event" it should fire straight after a successful retreival of a JWT...
Here is the action for retreiving a JWT in react:
export const requestLoginToken = (username, password) =>
(dispatch, getState) => {
dispatch({type: REQUEST_LOGIN_TOKEN, payload: username})
const payload = {
userName: username,
password: password,
}
const task = fetch('/api/jwt', {
method: 'POST',
body: JSON.stringify(payload),
headers: {
'Content-Type': 'application/json;charset=UTF-8'
},
})
.then(handleErrors)
.then(response => response.json())
.then(data => {
dispatch({type: RECEIVE_LOGIN_TOKEN, payload: data})
saveJwt(data)
})
.catch(error => {
clearJwt()
dispatch({type: ERROR_LOGIN_TOKEN, payload: error.message})
})
addTask(task)
return task
}
I beleive the place for adding a second API call would be after "saveJwt()" but how.
Do/can I send it off to another export const action/function in another part of the application?
If I write something similar to this and by putting the name of the function in with a parameter of "JWT" eg
.then(retrieveSelectData)
that it will go off to that separate folder with that export function and execute an API call at the same time applying a dispatch...and then return..
Could some one outline if this is a correct and reasonable way of making two API calls as one. I still have to get the JWT (and use it in the second) so I cant do the second call without the first.
If i understand your goal correctly, what you need here is a middleWare that will catch all actions that dispatched before the reducers catches them and can accept functions and holds a ref to the dispatcher.
Note that reducers can only accept actions that are plain objects and can't accept functions.
Enters redux-thunk, a middleware that does just that (and more) in only 11 lines of code.
It catches all actions (before the reducers) and checks to see if this action === 'function'.
If it is a function then it will call that function with dispatch as the argument.
If it's not a function it will leave it alone and let the reducers do their job.
Something like this:
function loadSomeThings() {
return dispatch => {
fetchFirstThingAsync.then(data => { // first API call
dispatch({ type: 'FIRST_THING_SUCESS', data }); // you can dispatch this action if you want to let reducers take care of the first API call
return fetchSecondThingAsync(data), // another API call with the data received from the first call that returns a promise
})
.then(data => {
dispatch({ type: 'SECOND_THING_SUCESS', data }); // the reducers will handle this one as its the object they are waiting for
});
};
}

Redirect to another route when `mapHooks` fails

I have defined following mapHooks:
const mapHooks = {
fetch: ({ dispatch, params }) => dispatch(fetchPost(params.postId)).catch(() => {
console.log('catch');
}),
};
Sometimes, post cannot be found and I'd like to redirect user to the another page.
When I catch the error I tried to call push('/foo') to redirect to /foo page, but it does nothing (push comes from react-router-redux.
How can I redirect user properly ?
push function that comes with react-router-redux is an action creator, meaning that it returns an action that you need to dispatch. So you need to call it like this: dispatch(push('/foo'))
Also, check if you have your routerMiddleware installed correctly.

Should action talk with store?

I am using react with flux architecture and I have a problem I face it.
I need to create an action that gets a user id and fetches the user. Here is the code:
var createAction = require('common/scripts/actions-helpers/create-action'),
resource = require('common/scripts/resources/conversation');
module.exports = createAction(fetchAction);
function fetchAction(context, payload, success, failure) {
resource.sync(context, payload.userId)
.then(function(user) {
context.dispatch('USER_FETCH', user);
success();
}, failure);
}
I want to use a store that will cache all users so in case the user fetched before, the action will not perform a backend call. The new action should look like that:
function getFetchedUser() {
// <--------- HOW TO KNOW WHETHER USER FETCHED?
}
function fetchAction(context, payload, success, failure) {
var user = getFetchedUser();
if (user) {
context.dispatch('USER_FETCH', user);
success();
} else {
resource.sync(context, payload.userId)
.then(function(user) {
context.dispatch('USER_FETCH', user);
success();
}, failure);
}
}
The issue is that I don't want to manage users data in the action so the only way come in my mind to implement getFetchedUser() is checking in the Users store.
Is this a good approach?
Can action access to store?
The common approach is to do the backend call from the action and then dispatch an action when the request either succeeds or fails. This way you can populate your store with an action. This means that the store keeps synchronous and can just emit changes as they come along.
var AppDispatcher = require(<Flux AppDispatcher>);
var UserActions = {
fetchUser: function(payload) {
fetchUserFromBackendApi(payload)
.then(function (error, user) {
if(error) {
AppDispatcher.dispatch({
actionType: "ERROR_CODE",
error: error
});
return;
}
AppDispatcher.dispatch({
actionType: "SUCCESSFUL_USER",
user: user
});
}
}
};
module.exports = UserActions;
Then you just handle different action types in the store and just populate your cache. When the cache is populated you emit a change event and the data will be rendered in.
This code ain't the prettiest so howl out if I misunderstood or if there are any questions.
So to answer the intial answer. The action component should not talk to the store. It goes against one of the key concepts of React, loose coupling.
With Flux and React the data flow works out because the components will fire actions when they need data. The actions just fetch the data.
I also had a similar problem. I have a PhotosStore that keep all photos from a user, server send it paginated.
User can reorder photos by more recently, more commented, more viewed. Is PhotosStore has all page loaded, it can reorder by itself, if not, need call server.
# actions
Actions.Photo = createActions(
'sortBy', {'load': asyncResult: true}
)
# store
Store.Photos = Reflux.createStore(
listenables: Actions.Photo
onSortBy: (order) ->
if #hasMorePages()
Actions.Photo.load({order: order})
else
# reorder and trigger
onLoadCompleted: (data) ->
# get data and trigger
)
# view
PhotoHeaderOrder = React.createClass
mixins: [
Reflux.connect(Store.Photos, 'model')
]
onSortNew: -> Actions.Photo.sortBy('news')
onSortCom: -> Actions.Photo.sortBy('comments')
onSortVie: -> Actions.Photo.sortBy('views')
render: ->
# render code

Categories

Resources