Where is fetched data saved in React? - reactjs

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

Related

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.

How to wait for getDownloadURL to finish in my mapping function before updating my object array in react state?

getImages() {
const entries_copy = this.state.entries;
entries_copy.map(entry => {
storage.refFromURL(entry.sign_in_photo).getDownloadURL()
.then((url) => {
entry["inPhotoURL"] = url;
storage.refFromURL(entry.sign_out_photo).getDownloadURL()
.then((url) => {
entry["outPhotoURL"] = url;
});
}).catch((error) => {
// Handle any errors
});
});
this.setState({entries: entries_copy});
}
I'm trying to retrieve the download url for images and store them in my entry object inside my entries object array but the problem I'm facing right now is that the setState is called before the urls are retrieved and I have no idea how to wait for it to complete before setting the state. I have searched for similar problems but most of them are solved by executing it inside then() but for mine, I can't execute it inside then() because I have to wait for all the entries to be updated. I have only recently started using React for this project so I'm sorry if the answer is obvious.
This is because the code in asynchronous.
You should call setState inside the .then() function.
I would recommend you to read about Promises in Javascript. They are an important aspect of the language to master.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
In addition to the answer of #TomSlutsky, note that you need to correctly chain your promises and you should not forget to "always return results, otherwise callbacks won't catch the result of a previous promise".
So you need to do as follows:
storage.refFromURL(entry.sign_in_photo).getDownloadURL()
.then((url) => {
entry["inPhotoURL"] = url;
return storage.refFromURL(entry.sign_out_photo).getDownloadURL()
})
.then((url) => {
entry["outPhotoURL"] = url;
this.setState(...);
})
.catch((error) => {
// Handle any errors
});
Note also how the catch() method is called at the end of the chain, see the doc for more details (and possible other options).

React Apollo Client - modify query data before it goes to cache

Is there a way to modify query response data before it is saved in the internal cache?
I'm using apollo hooks, but this question is relevant to any of front-end approaches using apollo client (HOC & Components as well).
const { data, updateQuery } = useQuery(QUERY, {
onBeforeDataGoesToCache: originalResponseData => {
// modify data before it is cached? Can I have something like this?
return modifiedData;
}
});
Obviously onBeforeDataGoesToCache does not exist, but that's exactly the behavior I'm looking for. There's an updateQuery function in the result, which basically does what is needed, but in the wrong time. I'm looking for something to work as a hook or a middleware inside the query mutation.
It sounds like you want Afterware which, much like Middleware that allows operations before the request is made, allows you to manipulate data in the response e.g.
const modifyDataLink = new ApolloLink((operation, forward) => {
return forward(operation).map(response => {
// Modify response.data...
return response;
});
});
// use with apollo-client
const link = modifyDataLink.concat(httpLink);

how to append the multiple api's

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.

How to have a server return a remote json object to client? react/express/web3

I'm using web3 to send a transaction to Ethereum. My express server logs the block, transactionHash, etc. data as a json object. I need the json returned to the client.
This question is running the risk of repeating a previous question, but I believe that it is more refined, to the point, and ultimately a different question. These previous threads have helped me remove several errors from the code and zero in on what is actually happening.
How to return json data to a react state?
How to await a json return value (the return takes at least 30 seconds) before logging it? javascript/react/express
How to set state of a react component with a specific item from a returned json object?
The specific code that is returning a blank value instead of the json object is:
web3.eth
.sendSignedTransaction("0x" + serializedTx.toString("hex"))
.on("receipt", console.log, res.json());
Client Code:
axios
.post("http://ec2-54-67-28-69.us-west-1.compute.amazonaws.com:3000/")
.then(response => console.log(response.data, payment))
.catch(function(error) {
console.log(error);
})
I somehow need to get the json object inside the res.json() but putting it inside the function () does not work. Any suggestions?
Thanks!
Edit. I am pretty sure I need to use something from this:
web3.eth.sendTransaction({from: '0x123...', data: '0x432...'})
.once('transactionHash', function(hash){ ... })
.once('receipt', function(receipt){ ... })
.on('confirmation', function(confNumber, receipt){ ... })
.on('error', function(error){ ... })
.then(function(receipt){
// will be fired once the receipt is mined
});
Try this:
web3.eth
.sendSignedTransaction("0x" + serializedTx.toString("hex"))
.on("receipt", res.json);
The way you did it res.json() was called without parameters.

Resources