So I'm trying to build a news feed reader for my websites. it's pretty basic just 2 flatLists. One vertical that lists the articles, the other horizontal that lists the sites. When clicking on the horizontal items it is updating 2 different state settings. These 2 settings are set as extraData for the flatLists. The horizontal one rerenders after a press but the vertical one doesn't.
The onPress is updating both the site and selectedId states and while the sites flatList rerenders and shows the background color change, the artile flatList does not rerender with the new feed.
Here is the code.
import { StatusBar } from 'expo-status-bar';
import React, { useState, useEffect } from 'react';
import { StyleSheet, Text, Image, View, FlatList, TouchableOpacity, Avatar, Dimensions } from 'react-native';
import Header from './components/header';
import Article from './components/article';
export default function App() {
const [isLoading, setLoading] = useState(true);
const [articles, setArticles] = useState([]);
const [site, setSite] = useState('https://minedhash.com/feed');
useEffect(() => {
fetch('https://api.rss2json.com/v1/api.json?rss_url='+site)
.then((response) => response.json())
.then((json) => setArticles(json.items))
.catch((error) => console.error(error))
.finally(() => setLoading(false));
}, []);
const [selectedId, setSelectedId] = useState(2);
const nftImg = require('assets/NFT.png');
const mhImg = require('assets/MH.png');
const cntImg = require('assets/CNT.png');
const sntImg = require('assets/SNT.png');
const entImg = require('assets/ENT.png');
const bscImg = require('assets/BSC.png');
const ethImg = require('assets/ETH.png');
const fafImg = require('assets/FAF.png');
const pntImg = require('assets/PNT.png');
const cenImg = require('assets/CEN.png');
const siteList = [
{'id': 1, 'name': 'NFT News Today', 'avatar': nftImg, 'color': '#ffffff', 'bgcolor': '#6856dd', 'url': 'https://nftnewstoday.com/feed'},
{'id': 2, 'name': 'Mined Hash', 'avatar': mhImg, 'color': '#000000', 'bgcolor': '#ff5757', 'url': 'https://minedhash.com/feed'},
{'id': 3, 'name': 'Cardano News Today', 'avatar': cntImg, 'color': '#000000', 'bgcolor': '#3cc8c8', 'url': 'https://cardanonewstoday.com/feed'},
{'id': 4, 'name': 'Solana News Today', 'avatar': sntImg, 'color': '#000000', 'bgcolor': '#7783d4', 'url': 'https://solananewstoday.com/feed'},
{'id': 5, 'name': 'Elrond News Today', 'avatar': entImg, 'color': '#ffffff', 'bgcolor': '#090223', 'url': 'https://elrondnews.com/feed'},
{'id': 6, 'name': 'BSC Chain', 'avatar': bscImg, 'color': '#ffffff', 'bgcolor': '#2b2e35', 'url': 'https://bscchainnews.com/feed'},
{'id': 7, 'name': 'Ethereum News Today', 'avatar': ethImg, 'color': '#000000', 'bgcolor': '#d8cd51', 'url': 'https://ethereumnewstoday.net/feed'},
{'id': 8, 'name': 'Finance and Freedom', 'avatar': fafImg, 'color': '#ffffff', 'bgcolor': '#0000fe', 'url': 'https://financeandfreedom.org/feed'},
{'id': 9, 'name': 'Polkadat News Today', 'avatar': pntImg, 'color': '#ffffff', 'bgcolor': '#1e1e1e', 'url': 'https://polkadotnewstoday.com/feed'},
{'id': 10, 'name': 'Crypto Exchange News Today', 'avatar': cenImg, 'color': '#ffffff', 'bgcolor': '#0277bd', 'url': 'https://cryptoexchangenews.net/feed'},
]
const Item = ({ item, onPress, backgroundColor, textColor }) => (
<TouchableOpacity onPress={onPress} style={[styles.site, backgroundColor]}>
<Image style={styles.tinyLogo} source={item.avatar} resizeMode='cover' />
<Text style={[styles.title, textColor]}>{item.name}</Text>
</TouchableOpacity>
);
const renderItem = ({ item }) => {
const backgroundColor = item.id == selectedId ? "#373361" : "#05003a";
const color = 'white';
return (
<Item
item={item}
onPress={function(){
setSelectedId(item.id);
setSite(item.url);
}}
backgroundColor={{ backgroundColor }}
textColor={{ color }}
/>
);
};
return (
<View style={styles.container}>
{/* header */}
<Header />
<View style={styles.content}>
<View style={styles.articleList}>
{/* Content */}
{isLoading ? <Text>Loading...</Text> :
( <FlatList
data={articles}
renderItem={({ item }) => (
<Article item={item}/>
)}
keyExtractor={(item, index) => index.toString()}
extraData={site}
/>
)}
</View>
</View>
<View style={styles.siteBar}>
{/* siteBar */}
<FlatList
horizontal
data={siteList}
renderItem={renderItem}
keyExtractor={(item) => item.id.toString()}
extraData={selectedId}
/>
</View>
<StatusBar style='light' />
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
},
content: {
paddingLeft: 20,
paddingRight: 20,
},
articleList: {
marginTop: 10,
marginBottom: 10,
height: Dimensions.get('window').height - 190
},
siteBar: {
height: 140,
width: Dimensions.get('window').width,
backgroundColor: '#fff',
},
site: {
height: 200,
width: 110,
marginLeft: 5,
padding: 5,
borderColor: '#fff',
borderWidth: 1,
borderRadius: 10,
textAlign: 'center',
},
title: {
textAlign: 'center',
},
tinyLogo: {
height: 90,
width: 90,
marginLeft: 5,
marginTop: 5
}
});
As per the code observation, I can see there might dependency issue in useEffect code.
Have a try by adding site dependency in the useEffect dependency list.
try ex.
useEffect(() => {
fetch('https://api.rss2json.com/v1/api.json?rss_url=' + site)
.then((response) => response.json())
.then((json) => setArticles(json.items))
.catch((error) => console.error(error))
.finally(() => setLoading(false))
}, [site])
instead of
useEffect(() => {
fetch('https://api.rss2json.com/v1/api.json?rss_url=' + site)
.then((response) => response.json())
.then((json) => setArticles(json.items))
.catch((error) => console.error(error))
.finally(() => setLoading(false))
}, [])