Reputation: 129
I'm trying to send strings from my Express server as a JSON object, and render those JSON objects in my React app.
Right now it is just displaying a number for each of the 25 elements for some reason:
0123456789101112131415161718192021222324
In my Express app image_url
and post_url
are strings. This always returns strings:
const image_url = imageData.data[0].media.image.src;
const post_url = imageData.data[0].target.url;
globalImageList.push({
image: image_url,
post: post_url
});
}
Also in Express. This sends the object to the React app when this function's url, i.e. '/api' is visited.
if (resultCount === processList.length) {
res.send(JSON.stringify(JSON.parse(globalImageList)));
In my React app, my component starts with state where the apiResponse
is an empty string:
this.state = {
apiResponse: ""
Data is fetched from my Express app using fetch()
which is called in componentDidMount
. I store that JSON data in the apiResponse
field:
callAPI() {
fetch('/api/getList')
.then(res => res.json())
.then(res => this.setState({ apiResponse: res }))
}
The way I render that state is as shown and it does not show the URLs, it just enumerates the objects:
render() {
return(
<div className="App">
<p className="App-intro">{Object.keys(this.state.apiResponse).map(i => i)}</p>
</div>
)
}
If I write it as {(this.state.apiResponse).map(i => i)} I get an error that apiResponse is not a function.
If I write it as {this.state.apiResponse} I get an error that says "Objects are not valid as a React child (found: object with keys {image, post}). If you meant to render a collection of children, use an array instead."
I've also found I can modify .then(res => res.json()) to .then(res => res.text()) and render only {this.state.apiResponse} the text of all the strings will show on the screen, but I need control of the individual elements in JSON format (I believe).
Upvotes: 1
Views: 195
Reputation: 30390
There are a few issues here - one seems to be that the component is not correctly handling/rendering the "loading stage" of the data fetch sequence (ie, the point where fetch()
is busy downloading data from the server).
This problem is usually solved by displaying a loading message to the user while the fetch()
is in progress, rather than attempt to render the component as if the response data were already present. A simple solution in your case would be to make the following changes:
callAPI() {
/* Leave as is - use res.json() to parse response to JSON object */
fetch('/api/getList')
.then(res => res.json())
.then(res => this.setState({ apiResponse: res }))
}
render() {
/* Extract apiResponse object (optional) */
const { apiResponse } = this.state;
/* Assume that while apiResponse matches initial state, the fetch
request is still busy, so render a loading message */
if(!apiResponse) {
return <p>Loading</p>
}
/* Otherwise, we assume the apiResponse is populated with data from
the sever and proceed to enumerate and render */
return(
<div className="App">
<p className="App-intro">
{/* Enumerate values of response, render as strings */}
{Object.values(apiResponse).map((value, index) =>
(<p key={index}>{ JSON.stringify(value) }</p>)
)}
</p>
</div>
)
}
Note also that enumeration is over Object.values()
rather than Object.keys()
- this allows you to map the actual values of the apiResponse
object to a rendered result (rather than number keys). Hopefully that points you in the right direction :-)
Upvotes: 1