I am creating a chat based on simple get and post using some php and AngularJS. Since a chat requires data to be refreshed constantly , I was wondering what is the best way to refresh data? How I did it was
$scope.LoadData = function () {
$http.get('php/getchatmessages.php')
.then(
function (response) {
$scope.data.messages = [];
$scope.data.messages = response.data;
$scope.evid = $scope.data.messages[0].EventID;
},
function (response) {
alert(response.data);
}
);
$interval(function(){$scope.LoadData()},5000);
};
This not only cause screen flickers, but the speed of the refresh speeds up over time, taking a lot of processor power and slowly crashes the browser.
What is the best way to refresh data?
I would recommend looking into socket.io which enables real time communication via the WebSocket API. They also have a chat demo. I believe WebSocket's are the best way to refresh data in your case.
If WebSockets are not an option for you, then your method will work if you fix your current issues:
The speed of the refresh speeds up over time
This is because you are calling $interval(function(){$scope.LoadData()},5000); inside $scope.LoadData. Move it outside the function so it doesn't start a new interval every call.
Taking a lot of processor power and slowly crashes the browser
If you are simply replacing the exiting message array with the new from the response then remove $scope.data.messages = []; and you will stop the screen from "flickering".
As for the data issue, you should think about retrieving only new messages instead of getting all of them at once. This way you can simply append the new data to your messages array. This is dramatically improve performance by minimising response sizes.
The flicker speeds up because you use $interval in each invocation, which is PERIODIC and adds another invocation afterwards. Either use $timeout which only adds one invocation, or call $interval outside of LoadData.
Related
I am new to both React-JS and Oboe.js. I am trying to speed up loading of some JSON data by using Oboe to stream the results. Unfortunately I am unable to do an update state in the function block. So I try to call another function that does the stateSet. Below is a method I have tried but doesn't work. It errors out a mapping function that uses search-results to render it in a table.
var that = this;
oboe({
url: //url,
method: 'POST', // optional
body: //POST-DATA, // optional
})
.on('node', '*', function(things){
that.updateState(things);
// This callback will be called everytime a new object is
// found in the foods array.
console.log( 'Go eat some', things.id);
});
updateState = (props) => {
this.setState({search-result: props});
}
What I am not sure about is the right way of updating a state with oboe.js and React?
Is there a better library to use for streaming JSON data into React?
Recommended approach
If you have the ability to change things server-side, then I would not recommend using Oboe for this. Oboe is useful if your only alternative is to load a large JSON object and you would like to access that data before the whole thing can be parsed.
The best way to optimize loading a lot of data on a client is to send less data at a time and to make multiple requests. A web-socket is the best approach, and Socket.io is a good tool for doing that.
If you need to use Oboe
I'm working to put together an example of oboe.js + react for you to look at, though it's tricky as much of the activity of Oboe happens outside the React lifecyle. I'll update this answer with that example 👍
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.
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.
In my app, I make two ajax calls to for one piece of data. First I make a call to get a list of ecommerceIntegrations. Once I have those, I can then grab each of their respective orders.
My current code looks something like this:
componentDidMount: function() {
EcommerceIntegrationStore.addChangeListener(this._onIntegrationStoreChange);
OrderStore.addChangeListener(this._onOrderStoreChange);
WebshipEcommerceIntegrationActionCreators.getEcommerceIntegrations();
},
_onIntegrationStoreChange: function() {
var ecommerceIntegrations = EcommerceIntegrationStore.getEcommerceIntegrations();
this.setState({ecommerceIntegrations: ecommerceIntegrations});
ecommerceIntegrations.forEach(function(integration) {
WebshipOrderActionCreators.getPendingOrdersOfIntegration(integration.id);
});
},
_onOrderStoreChange: function() {
this.setState({
pendingOrders: OrderStore.getAllPendingOrders(),
pendingOrdersByIntegration: OrderStore.getPendingOrdersByIntegration()
});
}
I'm trying to follow Facebook's Flux pattern, and I'm pretty sure this doesn't follow it. I saw some other SO posts about nesting data with Flux, but I still don't understand. Any pointers are appreciated.
Everything here looks good except this:
ecommerceIntegrations.forEach(function(integration) {
WebshipOrderActionCreators.getPendingOrdersOfIntegration(integration.id);
});
Instead of trying to fire off an action in response to another action (or worse yet, an action for every item in ecommerceIntegrations), back up and respond to the original action. If you don't yet have a complete set of data, and you need to make two calls to the server, wait to fire the action until you have all the data you need to make a complete update to the system. Fire off the second call in the XHR success handler, not in the view component. This way your XHR calls are independent of the dispatch cycle and you have moved application logic out of the view and into an area where it's more appropriately encapsulated.
If you really want to update the app after the first call, then you can dispatch an action in the XHR success handler before making the second call.
Ideally, you would handle all of this in a single call, but I understand that sometimes that's not possible if the web API is not under your control.
In a Flux app, one should not think of Actions as being things that can be chained together as a strict sequence of events. They should live independently of each other, and if you have the impulse to chain them, you probably need to back up and redesign how the app is responding to the original action.
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'