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');
}
);
};
Related
I know that extraData is used to update flatlist when it changes but somehow it does not work in my case. I know I am doing something wrong but I need advice to figure that problem out.
Here is my flatList:
<FlatList
data={this.state.data}
extraData={this.state.data}
renderItem={this.renderPost}
keyExtractor={(item, index) => index.toString()}
onEndReached={this.loadMorePosts}
onEndReachedThreshold={0.5}
ListFooterComponent={this.renderFooter}
showsVerticalScrollIndicator={false}
refreshControl={
<RefreshControl
refreshing={this.state.loading}
onRefresh={this.loadNewerPosts}
/>
}
/>
and here is my deleteRequest that should remove one item from this.state.data:
deletePost = (index) => {
console.log(this.state.data.length);
let data = this.state.data;
data.splice(index, 1);
this.setState({ data: data }, () => {
console.log(this.state.data.length);
});
};
I even tried to put refresh in state and to change it every time I delete item and put it as extraData but nothing happens. What am I doing wrong?
This.state.data.length is changing so the data changes but flatList do not re-renders.
Do something like
deletePost = (index) => {
....
let data = [...this.state.data]; // here is a logic
data.splice(index, 1);
this.setState({ data: data }, () => {
console.log(this.state.data.length);
});
}
If you want to use the Pull To Refresh feature try something like this:
refreshFlatlist = () => {
this.setState(
{
refresh: true,
},
() => this.getDataHandler() // whatever updates your dataArray displayed in the list
);
this.setState({
refresh: false,
});
};
Now the flatlist RefreshComponent looks like this:
<FlatList
refreshControl={
<RefreshControl
refreshing={this.state.refresh}
onRefresh={this.refreshFlatlist}
/>
}
extraData={this.state.refresh}
data={this.state.data}
keyExtractor={(item, index) => item.id.toString()}
renderItem={({ item }) => ( ...
Don't use splice. Try below snippet it's working as expected.
deletePost = index => {
const newData = this.state.data.filter((item, i) => i !== index);
this.setState({
data: newData,
});
};
render(){
return (
<FlatList
data={this.state.data}
extraData={this.state.data}
renderItem={this.renderItem}
/>
);
}
deletePost = (index) => {
let data = this.state.data;
data.splice(index, 1);
this.setState({ data: [] }, () => {
this.setState({ data: data });
});
};
Thanks all. With your solutions I came up with this idea and it works perfectly.
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.
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.
I am working on a web dev using ReactJs, NodeJs and Mysql. I got problem in displaying fetching data using axios.
here is my API :
app.get('/enduser',(req, res) => {
let ENDUSER_QUERY = "SELECT * FROM enduser_tbl";
let query = dbConn.query(ENDUSER_QUERY, (err, results) => {
if(err) {
console.log(err)
} else {
return res.status(200).json({"status": 200, "err" : null, "response": results});
}
});
});
and I call the API in my reactjs page component
class ProjectList extends Component {
constructor(props){
super(props);
this.state = {
enduser_tbl : []
}
}
async componentDidMount() {
const Url = "http://localhost:4000/enduser"
await axios.get(Url)
.then( enduser_tbl => {
console.log(enduser_tbl.data)
this.setState({
enduser_tbl : enduser_tbl.data
})
})
}
render() {
const enduser_tbl = this.state;
return (
<Container>
{ enduser_tbl.map ((enduser, project_id) =>
<ListGroup>
<ListGroup.Item key={project_id}> {enduser.project_type} </ListGroup.Item>
</ListGroup>
)}
</Container>
)
}
}
export default ProjectList
I got no error in my terminal but many problem appears in Chrome. here is the response from chrome
Error in destructuring, missed curly brace const { enduser_tbl }
render() {
const { enduser_tbl = [] } = this.state;
return (
<Container>
{ enduser_tbl.map ((enduser, project_id) =>
<ListGroup>
<ListGroup.Item key={project_id}> {enduser.project_type} </ListGroup.Item>
</ListGroup>
)}
</Container>
)
}
For safe side:
const Url = "http://localhost:4000/enduser"
await axios.get(Url)
.then( { data: enduser_tbl = [] } => {
console.log(data)
this.setState({
enduser_tbl
})
})
enduser_tbl.data will be undefined,
it will be either enduser_tbl.response or enduser_tbl which you are setting in your state.
this.setState({
enduser_tbl : enduser_tbl.response
})
Two things, you are not getting the correct piece of the state in your render function. You also need to deal with that both the API call and setState are async, so you can't rely on it being defined when your component is rendered.
You can do it like this:
const enduser_tbl = this.state.enduser_tbl || [];
return (
<Container>
{ enduser_tbl.map ((enduser, project_id) =>
<ListGroup>
<ListGroup.Item key={project_id}> {enduser.project_type} </ListGroup.Item>
</ListGroup>
)}
</Container>
)
I think that you are trying to map an object because this.state is an object. Try changing the code as given below.
class ProjectList extends Component {
constructor(props){
super(props);
this.state = {
enduser_tbl : []
}
}
async componentDidMount() {
const Url = "http://localhost:4000/enduser"
await axios.get(Url)
.then( enduser_tbl => {
console.log(enduser_tbl.data)
this.setState({
enduser_tbl : enduser_tbl.data
})
})
}
render() {
const enduser_tbl = this.state.enduser_tbl;
return (
<Container>
{ enduser_tbl.map ((enduser, project_id) =>
<ListGroup>
<ListGroup.Item key={project_id}> {enduser.project_type} </ListGroup.Item>
</ListGroup>
)}
</Container>
)
}
}
This error rises because you are not passing array to map function please add below code and let me know is it work for you are not
componentDidMount = async (e) =>{
const url = "http://localhost:4000/enduser"
try{
const res = await axios.get(url)
this.setState({
enduser_tbl :res.data
})
}
ctach(ex){
console.log(ex)
}
}
First, check your API response it might look like this
{
"status": 200,
"response": [{...}, {...}]
}
then when receiving your data make sure you've set it to the state properly
e.g:
await axios.get(Url)
.then(enduser_tbl => {
this.setState({
enduser_tbl: enduser_tbl.response
})
})
finally, make sure that you've destructured it properly just like xdeepkav said e.g
const { enduser_tbl } = this.state;
The error the you're encountering is because enduser_tbl can't be read as mappable data/array. To make it clear here's an example of your error
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..