How to call multiple different apis using useEffect hook in React - axios

I have a concern regarding calling the apis using axios in useEffect hook. For example, I have a react page where I am showing the all the list of consignments. A consignment can have a user from user list, a carrier from carrier list, an account from account list, a status from status list and a service from service list. All these lists are enumerated data. So, in this page where I have to get all the enumerated list before rendering the page, because in the react component, I have to display them as dropdown, so that users can apply filter on top of that. But getting the list of those enumerated data, I have to call the separate api. For example, getting users I have to call /users api and getting customers I have to call /customers api. My concern is do I need to call them using a single useEffect hook using the axios. Because I have to hit the server multiple times for getting those enumerated data. If the number of lists of enumerated data increases then my api request to the server will also increase. I don't know what is the best practice to deal with this kind of situation. Whether to create a single api so that the server is hit only once and all the enumerated data are returned or have the separate api and hit the server request to get separately to enumerated data. And hitting the server multiple times to get the enumerated data does it impact performance on the client-side I mean some memory leak? Just need some advice on that. Thanks in advance.
useEffect(() => {
const loadData = async () => {
try {
dispatch(getLoad(true));
const services = await Axios.get("/Services");
const customers = await Axios.get("/Accounts/Customers");
const resCarrier = await Axios.get("/Accounts/Carriers");
const resStatuses = await Axios.get("/Status");
setFilterData((prev) => ({
...prev,
services: services.data,
customers: customers.data,
carriers: resCarrier.data,
statuses: resStatuses.data,
}));
dispatch(getLoad(false));
} catch (error) {
dispatch(getLoad(false));
}
};
}, []);

You can use axios.all
axios.all([
axios.get(`/Services`),
axios.get(`/Accounts/Customers`),
axios.get(`/Accounts/Carriers`),
axios.get(`/Status`)
])
.then(axios.spread((services, customers, carriers, status) => {
setFilterData((prev) => ({
...prev,
services: services.data,
customers: customers.data,
carriers: carriers.data,
statuses: status.data,
}));
}));

Related

A form having fields for personal info And also states, Cities fetching data from api... How to Make them work all togather without submitting form

Context
I have created a form which is taking user data like their name, phone no, address...
For the address I'm using an api that is fetching states to a drop-down and also the cities after selecting a particular state...
I've done the state part because it doesn't require a submit request.
Now, the problem is that...
For fetching cities I tried jQuery and ajax but that requires a post request and I have other details for the form too so how do I make the cities drop down fetch the data of selected state without submitting the form (POST REQUEST)?
Since nested form doesn't work in html it's difficult for me to find the solution..
Please help me on this..
You should make the fetch/ api call for the cities data on an event triggered by user over and the UI element which in your case should be the onChange event of the dropdown/select of the states.
const stateSelector = document.querySelector('.stateSelect');
stateSelector.addEventListener('change', function(e) {
getCitiesForState(e.target.value);
});
function getCitiesForState(state) {
fetch(`http://exapleapitogetallcitiesinstate/${state}`, {
"method": "GET",
})
.then(response => response.json())
.then(data => {
console.log("cities data", data)
// show cities dropdown, populate options in the select with the data
})
.catch(err => {
console.log(err)
});
}
Instead of vanilla js you can use jquery
for event handling
https://api.jquery.com/change/#example-0
and fetching data with
https://api.jquery.com/jquery.get/

Persist react-query pagination after unmount

So I've got a paginated query that looks like this:
const [filters, setFilters] = useState({
search_text: undefined,
page_size: 10,
page: 1,
});
return useQuery({
queryKey: ['users', { filters }],
queryFn: () => /* some fetch */,
keepPreviousData: true,
});
My problem is I want to know the last filters used when so when I unmount my component, I can return to it and have the exact same data AND filters. Obviously if I keep the filters as state I lose this state when the component unmounts. I was wondering if react-query can somehow help me with this?
Since this is a client state problem, react-query can’t really help here. You would need to move that client state up, either to a parent component, a global client state manager like zustand, or eventually the url - which works very well for search queries and pagination because it also gives you shareable urls for free.

Extremely high loading times - Requests not running async. Mongoose

Overview
I've built an application with Vue, Express and MongoDB (mongoose ORM).
On loading the landing page, a series of GET requests are made for various bits of data. The loading times are extremely high, I've recorded some times as high as 22s for a particular route. It's lead me to believe that my requests are running sequentially, despite specifying in my logic that everything should run async
I've tried reducing the size of the objects being returned from the requests as well as using the .lean() method. These attempts shaved off a couple of seconds, but the overall issue is not remotely sorted. Times are still stupid high. To give an example:
From This:
// Method to find all users
var users = await User.find({});
To:
// Method to find all users
var users = await User.find({}, "username, uid").lean();
On the page in question, there are about 5 main components. Each component is making a get request. One of these is a Chat Column and the code for it is as follows:
ChatCol.vue
beforeMount () {
this.$store.dispatch('retrieve_chat')
}
Store.js (am using Vuex store)
retrieve_chat (context) {
return new Promise((resolve, reject) => {
axios({
url: api.dev + 'api/v1/chat',
method: 'GET',
})
.then(res => {
context.commit('set_chat', res.data)
resolve(res);
}).catch(err => {
// alert(err)
reject(err);
})
})
},
Requests in this format are being made on all the components. About 5 of them in the page in question.
Backend / Server Code
To give some context into the requests being made.
The client will hit the route 'http://localhost:3000/api/v1/chat'
and the code that makes the request on the server is the following:
var Chat = require("../models/ChatMessage");
module.exports = {
// LIMIT CHAT TO 100 MESSAGES
async get_chat(req, res) {
Chat.find({}, function(err, messages) {
if (err) {
return res.status(500).send({
message: "Interval Server Error",
type: "MONGO_CHAT_DOCUMENT_QUERY",
err: err,
})
}
if (!messages) {
return res.status(400).send({
message: "Resource not found",
type: "MONGO_CHAT_DOCUMENT_QUERY",
details: "!messages - no messages found",
})
}
messages.reverse();
return res.status(200).json({
messages,
});
}).sort({"_id": -1}).limit(30);
},
}
If I look at the network tab on the chrome dev tools, this is how the requests appear. Apologies for the long winded post, I literally have no idea what is causing this
Important Note:
It was mentioned to me that mongodb has this feature where it locks when mutating the data, and I thought that might be the case, but there are no mutations taking place. It's just 3/4 get requests happening in parallel, albeit pretty big requests, but they shouldn't be taking as long as they are
Screenshot of the network tab:
(ignore the failed req, and some of the duplicate named requests)
StackOverflow sempais please help. It's a very big application and I don't know what the issue is exactly, so If I've missed out any details - Apologies, I'll clarify anything that needs clarity.
Large amount of base64 encoded data from a previously abandoned and poorly implemented image upload feature was being stored in each chat message as well as other places, causing large amounts of data to be loaded in and ultimately lead to huge loading times.
Thank you Neil Lunn.

make meteor restful api/web-service

I have created a new url/route in my app where I need to write a web-service. I need to write a service that deletes user according to the parameters passed in the service. For now, anyone should be able to call that service (will make it secure at later stage). App is built on meteor.
My url is : loaclhost:3000/deleteUser. Now one should be able to call my delete user function defined on this page and pass json structure data as an argument to it. If the data is valid, then the user should be deleted.
Using simple:rest package
Meteor.publish("delUser", function (a, b) {
UserDetails.remove({}); //delete user according to data received
}, {
url: "/testing/delUser", //url where third party will call the function
getArgsFromRequest: function (request) {
// Let's say we want this function to accept a form-encoded request
// with fields named `a` and `b`.
console.log('received : ' + JSON.stringify(request.body) );
var content = request.body;
// Since form enconding doesn't distinguish numbers and strings, we need
// to parse it manually
return [content.a, content.b];
}
})
How to access the function, delUser from a thrid party? I also need to add authentication at a later stage.
Personnally, I use this :
simple:rest
simple:json-routes
simple:rest-accounts-password
I find it easier to implement.
even iron:router comes with server side routes where you can build your own functions and api calls.
http://iron-meteor.github.io/iron-router/#restful-routes
Sample (Server side code) :
Router.map(function () {
this.route("api", {path: "/api/:paramsYouNeed",
where: "server",
action: function(){
this.response.writeHead(200, {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
});
if (this.request.method == 'POST') {
var response;
//do whatever you want to do
this.response.end(response);
}
}
});
The other user can call this by making a http.post request to the above url (http:www.a****a.com/api/params)
The easiest way to do this is use the restivus package.
https://atmospherejs.com/nimble/restivus
Restivus makes building REST APIs in Meteor 0.9.0+ easier than ever
before! The package is inspired by RestStop2 and Collection API, and
is built on top of Simple JSON Routes to provide:
A simple interface for creating REST APIs
Easy setup of CRUD endpoints for Mongo Collections
User authentication via the API
Optional login and logout endpoints
Access to this.user in authenticated endpoints
Custom authentication if needed
Role permissions for limiting access to specific endpoints
Works alongside the alanning:roles package - Meteor's accepted role permission package

Sails pubsub how to subscribe to a model instance?

I am struggling to receive pubsub events in my client. The client store (reflux) gets the data from a project using its id. As I understand it this automatically subscribes the Sails socket for realtime events (from version 0.10), but I don't see it happening.
Here's my client store getting data from sails
(this is ES6 syntax)
onLoadProject(id) {
var url = '/api/projects/' + id;
io.socket.get(url, (p, jwres) => {
console.log('loaded project', id);
this.project = p;
this.trigger(p);
});
io.socket.on("project", function(event){
console.log('realtime event', event);
});
},
Then I created a test "touch" action in my project controller, just to have the modifiedAt field updated.
touch: function(req, res){
var id = req.param('id');
Project.findOne(id)
.then(function(project) {
if (!project) throw new Error('No project with id ' + id);
return Project.update({id: id}, {touched: project.touched+1});
})
.then(function(){
// this should not be required right?
return Project.publishUpdate(id);
})
.done(function() {
sails.log('touched ok');
res.ok();
}, function(e) {
sails.log("touch failed", e.message, e.stack);
res.serverError(e.message);
});
}
This doesn't trigger any realtime event in my client code. I also added a manual Project.publishUpdate(), but this shouldn't be required right?
What am I missing?
-------- edit ----------
There was a complication a result of my model touched attribute, since I set it to 'number' instead of 'integer' and the ORM exception wasn't caught by the promise error handling without a catch() part. So the code above works, hurray! But the realtime events are received for every instance of Project.
So let me rephrase my question:
How can I subscribe the client socket to an instance instead of a model? I could check the id on the client side and retrieve the updated instance data but that seems inefficient since every client receives a notification about every project even though they only should care about a single one.
----- edit again ------
So nevermind. The reason I was getting updates from every instance is simply because at the start of my application I triggered a findAll to get a list of available projects. As a result my socket got subscribed for all of them. The workaround would be to either initiate that call via plain http instead of a socket, or use a separate controller action for retrieving the list (therefor bypassing the blueprint route). I picked the second option because in my case it's silly to fetch all project data prior to picking one.
So to answer my own question. The reason I was getting updates from every instance is simply because at the start of my application I triggered a findAll to get a list of available projects. As a result my socket got subscribed for all of them.
The workaround would be to either initiate that call via plain http instead of a socket, or use a separate controller action for retrieving the list (therefor bypassing the blueprint route). I picked the second option because in my case it's silly to fetch all resources data prior to selecting one.
Here's the function I used to list all resources, where I filter part of the data which is not relevant for browsing the list initially.
list: function(req, res) {
Project.find()
.then(function(projects) {
var keys = [
'id',
'name',
'createdAt',
'updatedAt',
'author',
'description',
];
return projects.map(function(project){
return _.pick(project, keys);
});
})
.catch(function (e){
res.serverError(e.message);
})
.done(function(list){
res.json(list);
}, function(e) {
res.serverError(e.message);
});
},
Note that when the user loads a resource (project in my case) and then switches to another resource, the client is will be subscribed to both resources. I believe it requires a request to an action where you unsubscribe the socket explicitly to prevent this. In my case this isn't such a problem, but I plan to solve that later.
I hope this is helpful to someone.