how to append the multiple api's - reactjs

I'm having multiple of api's. how to i get the output.here i'm added the sample snippet. in that abc is the component. next component xyz, pqr like that.
let str1="http://localhost:ip/abc?text="+this.state.content;
fetch(str1, {
method: "GET",
})
.then(res => res.json())
.then(res => {
this.setState({
res: res.abc
});
});

I'm going to make some assumptions. Assuming you want to make 3 different API requests at the same time, and use the results for React setState, you can do so with Promise.all:
const reqA = fetch(`http://localhost:ip/abc?text=${this.state.content}`);
const reqX = fetch(`http://localhost:ip/xyz?text=${this.state.content}`);
const reqP = fetch(`http://localhost:ip/pqr?text=${this.state.content}`);
Promise.all([reqA, reqX, reqP]).then(function(allResults) {
Promise.all(allResults.map(res => res.json())).then(function(
jsonResults
) {
console.log("Results", jsonResults);
// Parse, and call `setState` here
});
});
The snippet above will make XHR calls to the 3 URLs at the same time, collect its result, attempt to parse the response to JSON for all 3 of the responses, and collect the results of that. At this point, you can parse, and set the response in the state.
Note that this does not include logic for dealing with errors in any of the 3 requests. You should account for that. If your request URLs are as similar as the code snippet above, then perhaps you can define a function for constructing a URL given a "component". The snippet above also does not account for the possibility that your component may become unmounted while requests are still in-flight.

Related

RTK Query hooks - preventing polling to auto refetch when arg change

I'm trying to refresh a token in a Query hook, with the polling feature every 9 seconds:
"/App.tsx"
..
...
const [storedToken, setStoredToken] = useState(getStoredToken());
const { data, error, refetch } = useRefreshUserQuery(storedToken, {
pollingInterval: 9000,
// refetchOnMountOrArgChange: false // -> This has no effect
});
...
..
The problem is, it re-fetches instantly when the token is set with setStoredToken(token). The new token is passed as argument to the Query hook storedToken and refetch immediately (like an infinite loop).
That would be pretty neat to be able to do this. Is there any better way to refresh a token with polling?
I believe that issue is nothing to solve on RTK-Q level - it's a pretty common and expected "limitation" of hooks and rendering lifecycle architecture. And I feel that RTK-Q polling just won't fit your requirements here, of course, that you are trying to achieve - it's not actually polling in common sense. At least - it's conditional polling, which needs some more logic)
So I would solve this just by debouncing and useEffect:
const [storedToken, setStoredToken] = useState<string>(getStoredToken());
const [tokenDebounced] = useDebounce(storedToken, 9000);
const { data } = useRefreshUserQuery(tokenDebounced);
useEffect(() => {
if (data) {
setStoredToken(data);
// console.log(newToken);
}
}, [data]);
The useEffect content and data content may differ, but the overall idea should be clear.
useDebounce is from https://www.npmjs.com/package/use-debounce,
but your own implementations should work the same if you have some defined already.
Another idea, touching you AUTH setup a bit - is just avoid
const [storedToken, setStoredToken] = useState<string>(getStoredToken());
the part at all, and keep useRefreshUserQuery() without params.
Most likely and common is to store the token in localStorage or redux\other store, and define new baseQuery, based on fetchBaseQuery that will set header and\or to include cookies with credentials: "include" with a token from localStorage or redux\other store. Definitely, you will need to store it during the first AUTH then.
I think RTK-Q auth example reveals this case in some way also:
https://redux-toolkit.js.org/rtk-query/usage/examples#authentication
After you'll avoid that useState and query hook param - you'll be able to use polling with no issues:
const { data, error, refetch } = useRefreshUserQuery(undefined ,{
pollingInterval: 9000,
});
"Polling" here means "fetch X seconds after I have data", but of course you have to get the first data itself - and that is that first fetch. If you prevent that, polling will also never start.
Tbh., this is kind of a weird requirement and doing it like this will fill your cache with dozens of state entries.
I'd do something a little differently - solve it in the endpoint lifecycle.
This is untested pseudocode and you'll need to adjust it a bit:
function waitFor(ms) {
return new Promise(resolve => setTimeout(() => resolve("waited"), ms))
}
currentToken: build.query({
query() {
// whatever you need for the first token here
},
async onCacheEntryAdded(
arg,
{ updateCachedData, cacheDataLoaded, cacheEntryRemoved }
) {
try {
// wait for the initial query to resolve before proceeding
await cacheDataLoaded
while (true) {
const result = await Promise.race(waitFor(9000), cacheEntryRemoved)
if (result !== "waited") {
// cache entry was removed, stop the loop
break
}
// make a fetch call to get a new token here
const newToken = fetch(...)
updateCachedData((oldData) => newToken)
}
},
})
and then just
const result = useCurrentTokenQuery()
in your component

How can I post form data from react to an express.js server and use it as part of a url string

Just to make this clearer. I have a form where the user inputs a name which is passed to the apiUrl const to become part of the URL string. The form input data is passed through state as {this.state.input}. After the user inputs the form with the name he wants to search for, he clicks a button which calls the onButtonSubmit function that fetches the data with the updated apiUrl parameters. The catch is, this is running on the front-end and as this is a proprietary api, I don't want to expose the api key (which is part of the url) to the user.
I have set up an express.js server but I'm still unsure on how I can post the form data to it and then use that in the same manner used in my code below.
onButtonSubmit = () => {
const apiUrl = 'URLStringPart1' + this.state.input + 'URLStringpart2withAPIKey';
fetch(apiUrl)
.then(res => res.json())
.then(
result => {
this.setState({
isLoaded: true,
array1: result.array1,
array2: result.array2,
array3: result.array3,
array4: result.array4,
array5: result.array5,
route: 'fetched'
});
},
error => {
this.setState({
isLoaded: false,
error: error
});
}
);
}
So the output I'm looking for would follow something like this:
Post form data from frontend after the user submits it with a
button click
Through the backend, after the form data is posted, use it to update the apiurl and then fetch the data from the api(using axios perhaps)
Send the fetched data back to the frontend
I think you need to use prams, in your express server the API route should look like this: api/users/:name --> returns user with this name.
Then try fetching this API by sending the request to the server like this:
http://locahost:8000/api/users/${this.state.name}

What's the best way to store a HTTP response in Ionic React?

I'm developing an app with Ionic React, which performs some HTTP requests to an API. The problem is I need to store the response of the request in a local storage so that it is accessible everywhere. The way I'm currently doing it uses #ionic/storage:
let body = {
username: username,
password: password
};
sendRequest('POST', '/login', "userValid", body);
let response = await get("userValid");
if (response.success) {
window.location.href = "/main_tabs";
} else if (!response.success) {
alert("Incorrect password");
}
import { set } from './storage';
// Handles all API requests
export function sendRequest(type: 'GET' | 'POST', route: string, storageKey: string, body?: any) {
let request = new XMLHttpRequest();
let payload = JSON.stringify(body);
let url = `http://localhost:8001${route}`;
request.open(type, url);
request.send(payload);
request.onreadystatechange = () => {
if (request.readyState === 4 && storageKey) {
set(storageKey, request.response);
}
}
}
The problem is that when I get the userValid key the response hasn't come back yet, so even awaiting will return undefined. Because of this I have to send another identical request each time in order for Ionic to read the correct value, which is actually the response from the first request. Is there a correct way of doing this other than just setting timeouts everytime I perform a request?
You are checking for the results of storage before it was set. This is because your sendRequest method is calling an asynchronous XMLHttpRequest request, and you are checking storage before the sendRequest method is complete. This can be fixed by making sendRequest async and restructuring your code a bit.
I would suggest you instead look for examples of ionic react using hooks or an API library - like fetch or Axios. This will make your life much easier, and you should find lots of examples and documentation. Check out some references below to get started:
Example from the Ionic Blog using Hooks
Example using Fetch using React
Related Stack Overflow leveraging Axios

Spread Operator not copying results in React

I am trying to update setState in a for loop, but for some reason state isn't being copied it's just being replaced. There should be 2 clients, instead I am getting one. Can anyone tell me why this is happening? The console.log is returning both clients.
const handleViewClients = () => {
for (let i = 0; i < clients.length; i++) {
console.log(clients[i].clientid);
fetch("http://localhost:3005/all-clients/" + clients[i].clientid)
.then((response) => response.json())
.then((result) => {
console.log(result);
setBarbersClient({
...barbersClient,
client: result,
});
});
}
};
I have also tried this... The console.log is returning what I need
Promise.all(
clients.map((client) =>
fetch("http://localhost:3005/all-clients/" + client.clientid)
)
)
.then((resp) => resp.json())
.then((result) => {
console.log(result.username)
setBarbersClient({
...barbersClient,
client: result,
});
});
Here is the route from the server side
app.get("/all-clients/:clientid", (req, res) => {
db.NewClientsx.findOne({
where: {
id: req.params.clientid,
},
}).then((response) => {
res.json(response);
});
});
There some fundamental concepts of sync vs. async code that you aren't accounting for here. State changing (and fetching) is asynchronous, so it won't run until after this synchronous loop has finished being executed (during which the state value will remain unchanged). Also, it's a bad idea to change state in a loop, for this reason and others.
Fetch all the clients, then do one state change at the end with all the fetched data. You can utilise things like Promise.all and Promise.spread to achieve this. Here's an example of doing multiple fetches then dealing with the results in one batch: How can I fetch an array of URLs with Promise.all?
You're making two distinct mistakes of which either is enough to cause the behaviour you're seeing.
1. You're overwriting the client property.
Every time you call the setter function you're overwriting the previous value of the client property. You'll need some data structure that supports multiple values like a map:
setBarbersClient({
...barbersClient,
clients: {
...barbersClient.clients,
[result.id]: result
},
});
You will need to change your render logic somewhat to accomodate the new data structure.
2. You're using a stale reference.
When you access barbersClient its setter may have already been called with a different value and your reference to it still refers to the value of the previous run of the render function. You can make sure your reference is fresh by using a set state action callback.
setBarbersClient(previousValue => {
...previousValue,
clients: {
...previousValue.clients,
[result.id]: result
},
});
previousValue will never be stale inside the set state action function body.

Where is fetched data saved in React?

I am trying to understand this snipped of code at an intrinsic level:
fetchAllData(){
fetch('http://ec2-x-x-xx-xx.xx-west-x.compute.amazonaws.com:3001/', {mode: "no-cors"})
.then(res => {
return res.json();
})
to better understand a simple component like this:
componentDidMount() {
this.fetchAllData();
}
fetchAllData(){
fetch('http://ecx-x-x-xxx-xx.xx-west-x.compute.amazonaws.com:3001/', {mode: "no-cors"})
.then(res => {
return res.json();
})
.then(resJson => {
this.setState(prevState => {
return{
fetchDataLoaded: true,
fetchData: resJson.data.todolist,
};
});
});
}
When fetching from an API, is the data stored temporarily in the res
=> function and chained on using .then?
If so, how could I visualise (in the console maybe?) the properties of the data fetched?
I find myself in a position where I need to manipulate data pulled from an API I don't know the shape of.
I am new to React and any detailed explanation would help a lot, thank you.
This isn't a react thing at all, but rather plain javascript and promises. fetch returns a resolved promise. The response isn't "saved" in res, per se, but rather is passed to a function where you've named the parameter res. If you want to view the raw response res you can do that in the first chained then, ensuring you still return the json promise for the next thenable.
fetch('http://ec2-3-8-196-93.eu-west-2.compute.amazonaws.com:3001/', {mode: "no-cors"})
.then(res => {
console.log('res', res);
return res.json();
})
Perhaps it would be a little clearer broken down a bit. Factor out the anonymous inline function into a named one, and pass that as the thenable callback. The fetch result isn't really saved anywhere (it is technically in memory in the browser heap, but that's another topic) and is just being passed to a function.
const logResultAndReturnJson = result => {
console.log('result', result);
return result.json();
};
fetch('http://ec2-3-8-196-93.eu-west-2.compute.amazonaws.com:3001/', {mode: "no-cors"})
.then(logResultAndReturnJson)
If you need to manipulate the fetched data, then you likely want to look at the resolved JSON object and not the response object.
In the given example, the variable resJson contains the response body parsed by JSON(i.e. this piece of code only works if the API returns a JSON response).
Adding on to #drew, This .then(...).then(...) is called Promise Chaining. It is a useful way of making a flow where you can process data in stages and then deal with errors in the end.
As Reference, these two pages will surely help
promise-basics
promise-chaining

Resources