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')
}
Related
Is it possible to make two backend requests at once from react?
The code below is the first backend call. The post request gets send to the backend and then I would like to do another request. Is it possible at all? Or do I have to wait for the backend response until the next request could be made?
What I basically want is to get information about how many files have been uploaded. The upload could take 3 minutes and the user right now only sees a loading icon. I want to additionally add a text like "50 of 800 Literatures uploaded" and 10 seconds later "100 of 800 litereratures uploaded".
This is basically my code :
class ProjectLiterature extends Component {
constructor(props) {
super(props);
this.state = {
isLoading:"false",
}
}
addLiterature(data, project_name) {
this.setState({ isLoading:true }, () => {
axios.post("http://127.0.0.1:5000/sendLiterature", data })
.then(res => {
this.setState({ isLoading: false })
})
})
}
If both requests do not depend on each other, you can make use of JavaScript's Promise.all() for the above purpose.
const request1 = axios.get('http://127.0.0.1:5000/sendLiterature');
const request2 = axios.get(url2);
Promise.all([request1,request2]).then([res1, res2] => {
// handle the rest
}).catch((error) => {
console.error(error);
// carry out error handling
});
If the second request relies on the response of the first request, you will have to wait for the first request to be completed as both requests have to be carried out in sequence.
const res = await axios.get('http://127.0.0.1:5000/sendLiterature');
// carry out the rest
You can see axios docs for this purpose, they support multiple requests out of box.
You can use Promise.all instead of axios.all as well but if one of requests fails then you won't be able to get response of successful calls. If you want get successful response even though some calls fails then you can use Promise.allSettled.
I have a search bar with a button. when the button is clicked, the search_on_click function runs. This function is supposed to retrieve the html of the URL and display it on the page.
class App extends Component{
constructor(props){
super(props)
this.state = {
html: []
}
this.search = this.search.bind(this)
}
search(){
const URL = 'https://www.zerochan.net/Re%3AZero+Kara+Hajimeru+Isekai+Seikatsu?s=fav'
axios.get(URL)
.then(data =>{
console.log(data)
this.setState({
query: data
})
})
.catch(function(error){
console.log(error);
})
}
render(){
return(
<div>
<button onClick={() => this.search()}>search</button>
<div>{this.state.query}</div>
</div>
This is the code i have so far. The problems/questions are:
axios does not console.log the html or even seem to run
i have tried fetch/requests and the problems are more or less the same
is there a better way to do this?
i do not think the is a CORS problem because i have used CORS allowing chrome extension.
.catch() does not log anything to console either
thank you for your time.
The URL you are trying to get isn't a valid resource. You should hit a URL that returns some data, in your case its an HTML which has no API for transferring data or CORS enabled for you to access it. Therefore, you won't get the expected result and axios won't be able to deliver your request.
Example of a valid API request https://codesandbox.io/s/jv73ynwz05
componentDidMount() is invoked immediately after a component is
mounted (inserted into the tree). Initialization that requires DOM
nodes should go here. If you need to load data from a remote endpoint,
this is a good place to instantiate the network request.
componentDidMount() {
// Note the api in the URL path, which means it is a valid endpoint
axios.get('https://randomuser.me/api/?results=50')
.then(response => {
const data = response.data.results;
this.setState({ data })
})
.catch(error => {
console.log(error);
})
}
See a similar issue here: Enable CORS in fetch api
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
});
};
}
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.
I was working on adding cookies to my project, full source here, but I ran into an issue where I can't set the cookies properly. I made a new api route that just creates a cookie and sends an object to the client.
server/routes/todo.routes.js
router.get('/todos', (req, res) => {
res.cookie('mycookie', 'a_value')
return res.send([{id:'1',isCompleted:false,text:'something'}])
})
If I call this api route directly, the browser renders the object and the cookie is set. The problem is when I call this api via AJAX from a rendered page, I still get the same response, but cookies aren't set. NOTE: I export the router and do app.use('/api', exported_object_here), so the URL is /api/todos.
shared/actions/todo.actions.js
export const getTodos = () => {
return (dispatch) => {
return fetch('/api/todos')
.then(response => response.json())
.then(todo => dispatch(_receiveTodos(todo)))
.catch(err => dispatch(_errorHandler(err)));
}
};
I have no idea why the browser would act differently in that situation, especially with something so simple. Do you all have any clue what could cause this?
You need to set withCredentials on your XHR request (https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/withCredentials)