React render component asynchronously, after data is fetched - javascript

I need to render a component after data is fetched. If try to load data instantly, component gets rendered but no data is show.
class App extends React.Component {
//typical construct
getGames = () => {
fetch(Url, {})
.then(data => data.json())
.then(data => {
this.setState({ links: data });
})
.catch(e => console.log(e));
};
componentDidMount() {
this.getGames();
}
render() {
return (
<div className="App">
<Game gameId={this.state.links[0].id} /> //need to render this part
after data is received.
</div>
);
}
}

You could keep an additional piece of state called e.g. isLoading, and render null until your network request has finished.
Example
class App extends React.Component {
state = { links: [], isLoading: true };
getGames = () => {
fetch(Url, {})
.then(data => data.json())
.then(data => {
this.setState({ links: data, isLoading: false });
})
.catch(e => console.log(e));
};
componentDidMount() {
this.getGames();
}
render() {
const { links, isLoading } = this.state;
if (isLoading) {
return null;
}
return (
<div className="App">
<Game gameId={links[0].id} />
</div>
);
}
}

You can do like this using short circuit.
{
this.state.links && <Game gameId={this.state.links[0].id} />
}

Can we use the pattern of "Render-as-you-fetch" to solve the problem.
Using a flag to check whether loading is complete doesn't look like a clean solution..

Related

How to make an API call and display it in a Table With React?

I am trying to make a component that will display API data in a table. The API is hosted on heroku at: https://protravelete-api.herokuapp.com/getAllEvents Here is a sample of the format that the API is returning in case that helps:
[
{"date":"2020-10-16","distance":5,"email":"sampleemail1#gmail.com","location":"sampletown1","name":"testperson","pace":"7","time":"16:00"},
{"date":"2020-10-18","distance":15,"email":"sampleemail2#gmail.com","location":"sampletown2","name":"testperson2","pace":"6","time":"19:00"}
]
The current component code is copied and pasted below:
import React from 'react';
import './Table.css';
export class ShowAllEvents extends React.Component {
constructor(props) {
super(props)
this.state = {
events: []
}
}
componentWillMount() {
const url = "https://protravelete-api.herokuapp.com/getAllEvents";
fetch(url)
.then(response => response.json())
.then(content => content)
.then(events => {
console.log(events)
this.setState(events)
})
.then(console.log(this.state.events))
}
renderTableHeader() {
let header = Object.keys(this.state.events[0])
return header.map((key, index) => {
return <th key={index}>{key.toUpperCase()}</th>
})
}
renderTableData() {
return this.state.events.map((events, index) => {
const {date, distance, email, location, name, pace, time } = events //destructuring
return (
<tr key={name}>
<td>{name}</td>
<td>{location}</td>
<td>{date}</td>
<td>{time}</td>
<td>{distance}</td>
<td>{pace}</td>
<td>{email}</td>
</tr>
)
})
}
render() {
return (
<div>
<h1 id='title'>All Upcomming Events</h1>
<table id='events'>
<tbody>
<tr>{this.renderTableHeader()}</tr>
{this.renderTableData()}
</tbody>
</table>
</div>
)
}
}
Anyone out there who can help? I'm sure my problems are just stupid mistakes, I am very new to using React, so my code is pretty messy.
You forgot to return the response in the second the thenable chain, which return undefined to third then. events become undefined. Try like this.
UPDATED: Use componentDidMount for AJAX calls, recommended by React.
componentDidMount() {
const url = "http://127.0.0.1:5000/getAllEvents";
fetch(url)
.then(response => response.json())
// .then(content => content) // This can be optional if you don't map the response
.then(events => {
console.log(events)
this.setState({ events })
})
}
renderTableHeader() {
if (!this.state.events.length) return null
const header = Object.keys(this.state.events[0]) // Earlier, you're getting error here
return header.map((key, index) => {
return <th key={index}>{key.toUpperCase()}</th>
})
}
Use response.json() instead of response.text
componentWillMount() {
const url = "http://127.0.0.1:5000/getAllEvents";
fetch(url)
.then(response => response.json())
.then(contents => console.log(contents))
.then(events => this.setState(events))
.then(console.log(this.state.events))
}

How to fix TypeError: (0, _fetchNews.getHomeNews) is not a function in React Native?

I'm building a news reader app using React Native. However when I try to fetch the feed from a url it shows this error:
I'm running the latest version of React Native and Nodejs.
This shows when i run run-android:
This is my News.js:
class News extends Component {
state = {
articles: [],
refreshing: true
};
componentDidMount = () => {
this.fetchNews();
};
fetchNews = () => {
getHomeNews()
.then(articles => {
this.setState({ articles, refreshing: false });
})
.catch(() => this.setState({ refreshing: false }));
};
handleRefresh = () => {
this.setState({ refreshing: true }, () => this.fetchNews());
};
render() {
console.log(this.state.articles);
return (
<FlatList
data={this.state.articles}
renderItem={({ item }) => <Article article={item} />}
keyExtractor={item => item.url}
refreshing={this.state.refreshing}
onRefresh={this.handleRefresh}
/>
);
}
}
And this is my fetchNews.js:
const url = ` URL here`;
export async function getHomeNews() {
let result = await fetch(url).then(response => response.json());
return result.articles;
}
Can anyone show me where I messed up? Thank you for your time.

Re render React Native Flatlist on data fetch from API

I am working on a React Native app where i'm using Flatlist.I have a list to render.But i want to add items to the list which i'm getting from the API on button click. I can see my API data on the console but they are not rendering for some reason. Also the list page i'm talking about is a child component. Here's what it looks like:
class ProductList extends Component {
state = {
isSpinner: false,
newArr: []
};
onScrollEndDrag = async() => {
this.setState({ isSpinner: true });
return await fetch(
`myAPI`
)
.then(response => response.json())
.then(json => {
this.setState({ newArr: [...this.state.newArr, ...(json || [])] })
return json;
})
.catch(error => console.log(error));
}
this.setState({ isSpinner: false });
}
render(){
const list = [data1,data2,data3];
return(
<Fragment>
<FlatList
key={this.key}
data={[...list,...this.state.newArr]}
renderItem={this.renderItem}
/>
<Button
title='Load More'
onPress={this.onScrollEndDrag}>
</Button>
</Fragment>
)
}
}
What can be done to show the new API data with the existing list?
Since you are anyways using async await no need of again using .then and .catch instead you can do it the below way.
onScrollEndDrag = async() => {
this.setState({ isSpinner: true });
try {
const response = await fetch(`myAPI`);
const json = await response.json();
this.setState({
newArr: [...this.state.newArr, ...(json || [])],
isSpinner: false
})
} catch(error) {
console.log(error);
this.setState({ isSpinner: false });
}
}
Hope this helps.

React Native Flatlist not refreshing data on pull up

Here is my posts page code, it fetches post titles from my API on load and this works perfect. The problem is that once it's loaded if a new post is added to API and I pull up to refresh it doesn't get new posts even though my onRefresh function works because I can trigger an alert in it.
The only way I can get new posts in API to show after they were loaded is by reloading the application itself.
componentDidMount() {
this.fetchData()
}
constructor(props) {
super(props);
this.state = {
refreshing: true,
data: []
};
}
fetchData = () => {
const url = 'myAPIurl';
fetch(url)
.then(res => {
return res.json()
})
.then(res => {
const arrayData = [...this.state.data, ...res]
this.setState({
data: arrayData,
refreshing: false
});
})
.catch(error => {
console.log(error);
this.setState({ refreshing: false });
});
};
handleRefresh = () => {
this.setState(
{
refreshing: true
},
() => {
this.fetchData();
alert('Pulled Up to Refresh');
}
);
};
render() {
return (
<View>
<FlatList
refreshControl={
<RefreshControl
refreshing={this.state.refreshing}
onRefresh={this.handleRefresh}
/>
}
horizontal={false}
data={this.state.data}
keyExtractor={item => item.id}
renderItem={({ item }) =>
<View>
<Text>{item.title.rendered}</Text>
</View>
}
/>
</View>
);
}
}
When I pull up to refresh I get this warning: Two children with same key. Keys should be unique. This is weird because each post ID is unique. And even with this warning, the new posts that are in API don't show unless I re-load the application.
Change your handleRefresh function like below:
handleRefresh = () => {
this.setState(
{
refreshing: true,
data:[]
},
() => {
this.fetchData();
alert('Pulled Up to Refresh');
}
);
};

Chaining Fetch Calls React.js

I'm making an application where I have to grab certain data from the Github API. I need to grab the name, url, language and latest tag. Because the latest tag is in a separate url, I need to make another fetch call there to grab that data.
I'm running into a certain amount of errors.
1st being the typeError cannot read property 'name' of undefined. I'm sure this is from the fetch call to the tag url where there isn't any data. I'm not really sure how to check if it's undefined. I've tried calling checking to see if the typeof data is undefined and so on but still get the error.
2nd problem being my tag url data doesn't show up with the other data. I'm sure I'm chaining the data wrong because when I click the add button it shows up.
Here is my code:
import React, { Component } from 'react'
import './App.css'
class App extends Component {
state = {
searchTerm: '',
repos: [],
favourites: []
}
handleChange = e => {
const { searchTerm } = this.state
this.setState({ searchTerm: e.target.value })
if (searchTerm.split('').length - 1 === 0) {
this.setState({ repos: [] })
}
}
findRepos = () => {
const { searchTerm } = this.state
// First api call here
fetch(`https://api.github.com/search/repositories?q=${searchTerm}&per_page=10&access_token=${process.env.REACT_APP_TOKEN}
`)
.then(res => res.json())
.then(data => {
const repos = data.items.map(item => {
const { id, full_name, html_url, language } = item
const obj = {
id,
full_name,
html_url,
language,
isFavourite: false
}
// Second api call here. I need the data from map to get the tags for the correct repo
fetch(`https://api.github.com/repos/${full_name}/tags`)
.then(res => res.json())
.then(data => {
obj.latest_tag = data[0].name
})
.catch(err => console.log(err))
return obj
})
this.setState({ repos })
})
.catch(err => console.log(err))
}
render() {
const { searchTerm, repos, favourites } = this.state
return (
<div className="App">
<h1>My Github Favorites</h1>
<input
type="text"
placeholder="search for a repo..."
value={searchTerm}
onChange={e => this.handleChange(e)}
onKeyPress={e => e.key === 'Enter' && this.findRepos()}
/>
<button
type="submit"
onClick={this.findRepos}>
Search
</button>
<div className="category-container">
<div className="labels">
<h5>Name</h5>
<h5>Language</h5>
<h5>Latest Tag</h5>
</div>
// Here I list the data
{repos.map(repo => (
<div key={repo.id}>
<a href={repo.html_url}>{repo.full_name}</a>
<p>{repo.language}</p>
{repo.latest_tag ? <p>{repo.latest_tag}</p> : <p>-</p>}
<button onClick={() => this.addToFavs(repo)}>Add</button>
</div>
))}
<h1>Favourites</h1>
{favourites.map(repo => (
<div key={repo.id}>
<a href={repo.html_url}>{repo.full_name}</a>
<p>{repo.language}</p>
<p>{repo.latest_tag}</p>
<button>Remove</button>
</div>
))}
</div>
</div>
)
}
}
export default App
If you use Promise.all(), you could rewrite your code like the following.
findRepos = () => {
const { searchTerm } = this.state;
// First api call here
const first = fetch(
`https://api.github.com/search/repositories?q=${searchTerm}&per_page=10&access_token=${
process.env.REACT_APP_TOKEN
}`
);
// Second api call here. I need the data from map to get the tags for the correct repo
const second = fetch(`https://api.github.com/repos/${full_name}/tags`);
Promise.all([first, second])
.then((res) => Promise.all(res.map(r => r.json())))
.then([data1, data2] => {
data1.then((firstData) => {
/*Do something you want for first.*/
});
data2.then((secondData) => {
/*Do something you want for second.*/
});
})
.catch((err) => console.log(err));
};
Hope this works for you.

Categories