update state with data return undefined - reactjs

I have one state called data that is empty object state={data:{}} I called RestAPI updated state USING set state when I console log it returned the data in the correct format but data that I need is this.state.data.title.rendered it says undefined but in console log it has data
Rest Api is like this:
{id:5,title:{rendered:"home is good"}}
fetch(`http://localhost/react-wordpress/wp-json/wp/v2/posts/${this.props.match.params.id}`)
.then((response) => response.json())
.then((responseJson) => {
const { title, fimg_url, content } = responseJson;
this.setState({ title, fimg_url, content });
this.setState({ data: responseJson })
})
.catch((error) => {
console.error(error);
});
In rendered method {this.state.data.title.rendered} return undefined while {this.state.title.rendered} return correct

Yes. Maybe it is undefined before fetching from backend.
So you need to validate if it is okay to render it like below:
const { data } = this.state;
return (
<div>
{ data && data.title && data.title.rendered
&& ( <h1>data.title.rendered</h1> )
}
<div>
);

try like this
fetch(`http://localhost/react-wordpress/wp-json/wp/v2/posts/${this.props.match.params.id}`)
.then((response) => response.json())
.then((responseJson) => {
const { title, fimg_url, content } = responseJson;
this.setState({ data: {title, fimg_url, content} });
})
.catch((error) => {
console.error(error);
});

Related

React JS fetch data from multiple API function

I was tasked to create a front-end for a poc. I am not a front end developer but I chose to use React JS.
There is only one page fetching data from multiple API endpoints. API endpoints return a simple json object.
I managed to get that to work however my code is ugly af and I want to create a function to handle all of that but I can't seem to get it right. Here's my code
export default class Dashboard extends React.Component {
constructor(props) {
super(props);
this.state = {
group1: [],
group2: [],
group3: [],
isLoaded: false,
}
}
componentDidMount() {
const group1_url = "http://localhost/api/1"
const group2_url = "http://localhost/api/2"
const group3_url = "http://localhost/api/3"
fetch(group1_url)
.then(res => res.json())
.then(json => {
this.setState({
group1: json,
})
});
fetch(group2_url)
.then(res => res.json())
.then(json => {
this.setState({
group2: json,
})
});
fetch(group3_url)
.then(res => res.json())
.then(json => {
this.setState({
group3: json,
})
});
}
I am trying to create a function like this:
function fetch_data(url, state) {
fetch(url)
.then(res => res.json())
.then(json => {
this.setState({
state: json,
})
});
}
var group1 = fetch_data(group1_url, group1);
So far no joy. How can I create a function to fetch data and set a state in js?
Alternatively how can I make my code look better? Or is there something else I should use/look into?
Pass a string as the second parameter, and use a computed property:
function fetch_data(url, state) {
fetch(url)
.then(res => res.json())
.then(json => {
this.setState({
[state]: json,
})
});
}
fetch_data(group1_url, 'group1');
I'd also highly recommend catching errors - possible unhandled rejections should be avoided whenever possible.
You might want to use Promise.all to wait for all groups to load:
const dataSources = {
group1: 'http://localhost/api/1',
group2: 'http://localhost/api/2',
group3: 'http://localhost/api/3',
};
Promise.all(
Object.entries(dataSources).map(([propertyName, url]) => (
fetch(url)
.then(res => res.json())
.then((result) => {
this.setState({
[propertyName]: result
})
})
))
)
.then(() => {
this.setState({ isLoaded: true })
})
.catch((error) => {
// handle errors
})
(also note that your json argument is not a JSON - JSON is only a format that exists with strings. Something that has been deserialized is just a plain object or array. Better to call it something less misleading, like result as I did)
You could try Promise.all
Promise.all takes an array of promises (it technically can be any iterable, but is usually an array) and returns a new promise.
const points = [
"http://localhost/api/1",
"http://localhost/api/2",
"http://localhost/api/3",
];
const responses = await Promise.all(points.map((point) => fetch(point)));
const data = await Promise.all(responses.map((response) => response.json()));
const [group1, group2, group3] = data;
this.setState({
group1,
group2,
group3,
});
Just remember to wrap this logic in an async function
You can do something like this.
function fetch_data(url, state) {
fetch(url)
.then(res => res.json())
.then(json => {
this.setState({
[state]: json,
})
});
}
var group1 = fetch_data(group1_url, 'group1');

How to push data into state array in react native

Suppose I have an array of ids that reflects my images [aaa, bbb, ccc]and I will be using that data to get my images.
I want to use these ids and get my images and store them in an array state which would be used to display my images in flatlist.
My code is as follows.
getIDs() {
fetch(config)
.then((response) => {
return response.json();
})
.then(async (data) => {
data.map(async id => {
await this.setImages(id.id)
} );}) }
async setImages(imageID) {
await fetch(config ) //where image Id is used
.then((response) => {
return response.json();
} })
.then(async (data) => {
await this.setState({ images: this.state.images.concat(data.content), isLoading: false }
})
The problem with this setState is that it changes the sequence of my images. I want to load my images in a specific sequence that I am getting from my database.
My Flatlist is as follows
<FlatList
data={[...this.state.images]}
numColumns={3}
renderItem={({ item, index }) => {
return (
<Image source={{ uri: 'data:image/png;base64,' + item }} />
);
}}
keyExtractor={(item, index) => index.toString()}
/>
Instead of setState there for each image, first make an array to contain images and push image there so that theres no change in order and while retrieving do that like you used to do
let imagesArray=[];
getIDs() {
fetch(config)
.then((response) => {
return response.json();
})
.then(async (data) => {
data.map(async id => {
await this.setImages(id.id)
} );
}
this.setState({imagesArray:imagesArray})
) }
async setImages(imageID) {
await fetch(config ) //where image Id is used
.then((response) => {
return response.json();
} })
.then(async (data) => {
// await this.setState({ images: this.state.images.concat(data.content), isLoading: false }
imagesArray.push (data.content)
}) :,
hope it helps

How can I call another api in flat list?

I have one API which has title, body, and user-id, and I want to print User Name with the help of another API. So how can I do it?
I have listed both APIs in below.
https://jsonplaceholder.typicode.com/posts
https://jsonplaceholder.typicode.com/users/1 // ${id}
getUser(id) {
let userData = fetch(`https://jsonplaceholder.typicode.com/users/${id}`)
.then(response => response.json())
.then((responseJson) => {
return responseJson.name
})
.catch(error => console.log(error)) //to catch the errors if any
console.log(userData);
return userData;
};
what you can do is first get the posts and save it inside a variable .
then inside a map call other api to get the user data and append the user data in the variable
example
fetch('https://jsonplaceholder.typicode.com/posts',)
.then(response => {
let new_post = [];
let posts = response.json() ;
Promise.all(
posts.map(item => {
fetch('https://jsonplaceholder.typicode.com/users/'+item.userId)
.then(res =>{
item['user'] = res.json() ;
console.log(item)
new_post.push(item)
})
})
)
.then(() => {
this.setState({testing:new_post})
})
})
then in your flatlist display username by
<FlatList
data={this.state.testing}
renderItem={({item}) =>(
<View>
<Text>other</Text>
<Text>{item.user.email}</Text>
</View>
)
}
/>
OR if your are using axios to get data change code to
axios.get('https://jsonplaceholder.typicode.com/posts',)
.then(response => {})

react native get response from then and assign to variable

I am trying to get thumbnail path and storing to a variable to be used, But I am getting undefined
getThumbnail(filePath){
let thumbnailURL = RNThumbnail.get(filePath)
.then((response) => response.path)
.then((responseData) => {
console.warn(responseData);
return responseData;
}).catch(error => console.warn(error));
alert(thumbnailURL);
//return thumbnailURL;
}
.then doesn't work like that, it won't return a value. You could do:
let thumbnailURL;
RNThumbnail.get(filePath)
.then((response) => response.path)
.then((responseData) => {
thumbnailURL = responseData;
alert(thumbnailURL);
}).catch(error => console.warn(error));
but you have to continue computation inside the second then call because the value is only going to be reliable there
You're better off using async/await, just refactor your code to this:
async function getThumbnail(filePath){
try {
let thumbnailURL = await RNThumbnail.get(filePath)
alert(thumbnailURL)
} catch(err) {
console.warn(err)
}
read more about async / await
For React app, most likely you will want to set the response as state:
state = {
thumbnailURL: ''
}
getThumbnail = (filePath) => {
RNThumbnail.get(filePath)
.then(response => response.path)
.then(responseData => {
this.setState({
thumbnailURL: responseData
})
})
.catch(error => console.warn(error))
}
render() {
return (
<img src={this.state.thumbnailURL} />
)
}
You will need arrow function on getThumbnail for lexical binding so that you can access this.setState().
Edit:
You can't actually make getThumbnail() return thumbnailURL value right away. getThumbnail() can however return the promise, and you resolve it at the place where you want access to thumbnailURL:
getThumbnail = filePath => {
return RNThumbnail.get(filePath)
.then(response => response.path)
.then(responseData => responseData)
.catch(error => console.warn(error))
}
IWannaAccessThumbnailURLHere = () => {
this.getThumbnail('....')
.then(thumbnailURL => {
// do things with thumbnailURL
})
}
Or, use setState, re-render then you can access this.state.thumbnailURL in the next render-cycle.

React Multiple Fetch from state

so I am trying to fetch my SWAPI json-server twice using the homeworld id to get the homeworld name but I just get "TypeError: Cannot read property 'name' of undefined". Im fairly new to React so if it looks messy im sorry! Thanks in advance!
import React, { Component } from 'react';
import './Card.css';
const person = personID =>
`http://localhost:3008/people/${personID}`
const picture = pictureID =>
`http://localhost:3008/${pictureID}`
// const planet = planetID =>
// `http://localhost:3008/planets/${planetID}`
class Card extends Component {
constructor(props){
super(props);
this.state ={
requestFailed: false,
person: 1,
planet: 4
}
}
componentDidMount(){
fetch(person(this.state.person))
.then(response => {
if(!response.ok) {
throw Error("Network Request Failed");
}
return response
})
.then(d => d.json())
.then(d => {
this.setState({
cardData: d
})
}, () => {
this.setState({
requestFailed: true
})
})
fetch(`http://localhost:3008/planets/${this.state.cardData.homeworld}`)
.then(data => data.json())
.then(data => {
this.setState({
homeData: data
})
})
}
render() {
if(this.state.requestFailed === true) return <p>Error please try
again!</p>
if(!this.state.cardData) return <p>Loading ...</p>
return (
<div className='card'>
<div className='card-content'>
<div className='card-name'>{this.state.cardData.name}</div>
<img src={picture(this.state.cardData.image)}
alt='profile'/>
<p>
<span>Birthday:</span>
<span>{this.state.cardData.birth_year}</span>
</p>
<p>
{/* Note that in order to get the homeworld's name, you have to get the planet name from a different endpoint than the people */}
<span>Homeworld:</span>
<span>{this.state.homeData.name}</span>
</p>
</div>
</div>
);
}
}
export default Card;
It looks like the first problem is that you're attempting to render homeData.name before it's loaded. You should probably have a loading check similar to the cardData loading check. Or you could just render nothing until it's loaded:
{this.state.homeData ?
<span>{this.state.homeData.name}</span> :
<span>Loading...</span>
}
The 2nd problem is that you're doing the second fetch of homeData at the same time as the first fetch. So this line:
fetch(`http://localhost:3008/planets/${this.state.cardData.homeworld}`)
Will always fail because the cardData is not loaded into the state yet when it runs.
What you should do is move it inside the response part of the first request:
fetch(person(this.state.person))
.then(response => {
if(!response.ok) {
throw Error("Network Request Failed");
}
return response
})
.then(d => d.json())
.then(d => {
this.setState({
cardData: d
})
fetch(`http://localhost:3008/planets/${d.homeworld}`)
.then(data => data.json())
.then(data => {
this.setState({
homeData: data
})
})
}, () => {
this.setState({
requestFailed: true
})
})
It would help to extract these into separate functions to clean it up a bit.

Resources