say the code below:
this.props.getRemoteUserId()
this.props.getRemotePrice()
the first function will get the userId and then with that Id I can fetch the new price. My question is : is this.props.getRemotePrice() fired after the new userId has save to the store?
That really depends on what is inside those functions. If they are both asynchronous AJAX calls for instance, then you can never be sure which one will finish first.
Related
I have a problem and I'm pretty sure I'm not the only one who ever had it... Although I tried to find a solution, I didin't really find something that fits my purpose.
I won't post much code, since its not really a code problem, but more a logic problem.
Imagine I have the following hook:
useEffect(() => {
fetchFromApi(props.match.params.id);
}, [props.match.params.id]);
Imagine the result of fetchFromApi is displayed in a simple table in the UI.
Now lets say the user clicks on an entity in the navigation, so the ID prop in the browser URL changes and the effect triggers, leading to an API call. Lets say the call with this specific ID takes 5 seconds.
During this 5 seconds, the user again clicks on an element in the navigation, so the hook triggers again. This time, the API call only takes 0,1 seconds. The result is immediatly displayed.
But the first call is still running. Once its finished, it overwrites the current result, what leads to wrong data being displayed in the wrong navigation section.
Is there a easy way to solve this? I know I can't cancel promises by default, but I also know that there are ways to achieve it...
Also, it could be possible that fetchFromApi is not a single API call, but instead multiple calls to multiple endpoints, so the whole thing could become really tricky...
Thanks for any help.
The solution to this is extremely simple, you just have to determine whether the response that you got was from the latest API call or not and only then except it. You can do it by storing a triggerTime in ref. If the API call has been triggered another time, the ref will store a different value, however the closure variable will hold the same previously set value and it mean that another API call has been triggered after this and so we don't need to accept the current result.
const timer = useRef(null);
useEffect(() => {
fetchFromApi(props.match.params.id, timer);
}, [props.match.params.id]);
function fetchFromApi(id, timer) {
timer.current = Date.now();
const triggerTime = timer.current;
fetch('path').then(() => {
if(timer.current == triggerTime) {
// process result here
// accept response and update state
}
})
}
Other ways to handle such scenarios to the cancel the previously pending API requests. IF you use Axios it provides you with cancelToken that you can use, and similarly you can cancel XMLHttpRequests too.
What happen if I have multiple middleware (lets say 3 for the exemple), all catching a single action ? Do they trigger in the order defined in the store creation ?
createStore(reducer,applyMiddleware(middle1, middle2, middle3));
middle1 will get triggered first, then middle2, then middle3 ? (when calling next() )
Can I on a specific action force middle3 to be called before middle2 ?
The middleware pipeline exactly matches the order that you passed to applyMiddleware(). So, in that example:
calling store.dispatch() passes the action to middle
when middle1 calls next(action), it goes to middle2
when middle2 calls next(action), it goes to middle3
when middle3 calls next(action), it goes to the actual store and the reducer logic is executed
And no, you cannot reorder middleware after the store has been created.
As a preface, I'm still new to React, so I'm still fumbling my way through things.
What I have is a component that fetches data to render an HTML table. So I call my Actions' fetchData() (which uses the browser's fetch() API) from within componentWillMount(), which also has a listener for a Store change. This all works well and good, and I'm able to retrieve and render data.
Now the next step. I want to be able to fetch new data when the component's props is updated. But I'm not exactly sure what the proper way to do so is. So I have a three part question
Would the proper place to do my fetchData() on new props be in componentWillReceiveProps(), after validating that the props did change, of course?
My API is rather slow, so it's entirely possible a new prop comes in while a fetch is still running. Is it possible to cancel the old fetch and start a new one, or at least implement logic to ignore the original result and wait for the results from the newer fetch?
Related to the above question, is there a way to ensure only one fetch is running at any time besides having something like an isLoading boolean in my Action's state (or elsewhere)?
Yes, componentWillReceiveProps is the proper place to do that.
Regarding point 2 and 3:
The idea of cancelling the task and maintaining 'one fetch running' seems to be inadequate. I don't think this kind of solution should be used in any system because implementation would limit an efficiency of your app by design.
Is it possible to cancel the old fetch and start a new one, or at least implement logic to ignore the original result and wait for the results from the newer fetch?
Why don't you let a 'newer fetch' response override an 'old fetch' response?
If you really want to avoid displaying the old response you can implement it simply using a counter of all fetchData calls. You can implement it in this way:
var ApiClient = {
processing: 0,
fetchData: function(){
processing++
return yourLibForHTTPCall.get('http://endpoint').then(function (response)){
processing--
return response
}
},
isIdle: function(){
return processing == 0
}
}
and the place where you actually make a call:
apiClient.fetchData(function(response){
if(apiClient.isIdle()){
this.setState({
})
}
}
I hope yourLibForHTTPCall.get returns a Promise in your case.
I'm trying to replace a Backbone.Marionette App to React and am facing difficulty thinking about query params. I think I'm missing a really simple peace in understanding this pattern so I apologize if this question is totally nonsense. I would appreciate any support or just pointing me to some direction that I can google more specifically.
There's a /users page which lists users and you can filter the users via search bar. So if you want to filter the users which contain 'joe' in their username, I would make a request to the server with query params like /users?username=joe. In addition I am able to paginate by adding a page parameter, too (/users?username=joe&page=1).
If I only think about the functionality, the flow would probably be
The Client inserts joe to the input element and clicks Search.
Clicking the Search button fires an Action (like Action.getUser).
The Action makes a request to the server and receives the results
The Dispatcher dispatches a function with the results payload to whomever (usually the Store) is interested in the Action.
The Store's state changes with the new result received by the Action
The View (Component) re-renders by listening to the Store's change.
and it works as expected. However, I would like the Client to be able to bookmark the current filtered result and be able to come back to the same page some time later. This means I will need somewhere to save explicit information about the search term the Client made, which is usually the url (am I right?). So I will need to update the url with query parameters to save the search term (/users?username=joe&page=1).
What I'm confused is where and when to update the url? What I can come up with right now are the 2 options below - and they don't seem to be clean at all.
Option 1
The Client inserts joe to the input element and clicks Search.
Clicking the Search button fires a transition of the ReactRouter with the new query params (/users?username=joe&page=1).
The View (Component) receives the new params via this.props.params and this.props.query.
The View (Component) fires an Action like Action.getUser depending on the query params it receives - in this case username=joe&page=1.
after this, it is the same as above
Option 2 (only 6 is different from what I explained above)
The Client inserts joe to the input element and clicks Search.
Clicking the Search button fires an Action (like Action.getUser).
The Action makes a request to the server and receives the results
The Dispatcher dispatches a function with the results payload to whomever (usually the Store) is interested in the Action.
The Store's state changes with the new result received by the Action
The View (Component) re-renders by listening to the Store's change. And somehow (I don't know how, yet) updates its url depending on its props (like this.props.searchusername, and this.props.searchpage)
What is the best practice on handling query params? (or this may not be specific to query params)
Am I completely misunderstanding the design pattern or architecture? Thanks in advance for any support.
Some articles I've read
Any way to get current params or current query from router (outside of component)?
Async data and Flux stores
Make it easier to add query parameters
React Router and Arbitrary Query Params: Page Refreshes Unintentionally on Load?
Add default params?
I would consider best practice to be the submit button only setting the location query (username). The rest should be taken care by the main react component that is assigned as router component. By this, you can be sure that anytime one revisits or shares the url, they can get the same results. And this is very generic too.
Something like this:
let myQuery = this.props.location.query;
if (myQuery.username) {
let theUser = myQuery.username;
this.setState({
userName = myQuery.username
});
} else {
this.setState({
userName = false //Show All
});
}
And then use this state "userName" to send to the server to search with. By this way, you will not need to iterate the code of the component that takes care of listing users since server already sends the relevant data.
In my experience with using location queries in React, I have been very content with their reactivity cycles and performance. I'd highly recommend keeping every different app state in relevance with the url.
Not entirely sure what you mean by
this means I will need to update the url to save the information (/users?username=joe&page=1).
You will probably have a similar structure to this.
TopContainer.jsx
-- Users.jsx
-- a list of User.jsx
Usually TopContainer will watch all the stores and if anything changed, pass it down to users.jsx. That way in Users.jsx, you can simply render this.props.users without worrying about any reRendering.
The search users actions usually happens in TopContainer's componentWillMount event, and you the page will listen to UserStore. That's a good place to throw in any query params. Something like this would work
componentWillUnmount() {
let searchTerm = router.getCurrentQuery().searchTerm;
UserActions.searchUsers(searchTerm)
},
The page doesn't really care if the url has a query params or not, it just dumbly shows whatever in the user store.
Then when the search finishes, Users.jsx will be reloaded and show the correct results
I am new to angular and I am building an app where I want to make multiple API calls and update the view as the data from them comes by. I do not want to wait for all the api calls to be completed to update my view and my api calls are not dependent on each other. Some of the API calls takes more than a minute to return the data.
I was thinking of using $q.all since I can start multiple asynchronous tasks, but I can't update the view after each one is completed. Could someone please point out how I can build this ?
Should I just use $scope.$apply in the success block of my $http call ?
My progress so far LINK (this was different issue I had, but the code is the same)
It's a bit hard to understand your model and what you're trying to achieve from you question, but you might want to use something like $broadcast() and $on().
So you'd broadcast an event when you're API has finished downloading:
$scope.$broadcast('API-download', data);
and then listen for it elsewhere and update your view
$scope.$on(
'API-download',
function(data){
processData( data );
}
)
That syntax might not be perfect, and as you have multiple API calls you'll need to broadcast different events like 'API-product-download' and 'API-catalogue-download'