stop activity indicator when all data has been fetched from server - react-native

I'm getting activity indicator after 20 posts as the offset number is set to 20 and after each scroll its loading more content but I want it to stop loading (Activity Indicator) when reached at the end of the data and there is no data to fetch.
Here is all the default states:
this.state = {
data: [],
dataProvider: new DataProvider((item1, item2) => {
return item1.ID !== item2.ID;
}),
isLoading: false,
};
Here is the render of the component:
render(){
if( !this.state.data.length ) {
return(
<ActivityIndicator
style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}
size='large'
color='#0A80FE'
/>
);
}
return(
<>
<View style={{flex:1}}>
<RecyclerListView
style={{ flex: 1 }}
onEndReached={() => {
this.getData();
//(ignore this as this is required for recyclerlist view)
this.setState({});
}}
onEndReachedThreshold={1000}
dataProvider={this.state.dataProvider}
rowRenderer={this.renderItem}
renderFooter={this.renderFooter}
/>
</View>
</>
);
Here is the getData function:
getData = () => {
if(this.state.isLoading){
return;
}
this.setState({
isLoading: true
});
const url = 'some url?offset=' + this.state.data.length;
fetch(url).then((response) => response.json())
.then((responseJson) => {
this.setState({
data: this.state.data.concat(responseJson.posts),
dataProvider: this.state.dataProvider.cloneWithRows(
this.state.data.concat(responseJson.posts)
),
})
})
.catch((error) => {
console.error(error);
}).finally(() => {
this.setState({
isLoading: false,
})
})
}
Here's renderItem function:
renderItem = (type, item ) => (
<ListItem
containerStyle={{height: 120}}
title={item.title}
subtitle={item.author.name}
leftAvatar={avatar}
bottomDivider
/>
);
And here is renderFooter function:
renderFooter = () => {
return !this.isLoading
? <ActivityIndicator
style={{ marginVertical: 10 }}
size='large'
color='#0A80FE'
/>
: <View style={{ height: 60 }}>
<Text style={{color: '#ccc', textAlign: 'center', paddingVertical: 10,}}> You've reached at the end of the posts.</Text>
</View>;
};
renderFooter always sets to loading even if I reached at the end of the posts resulting in an unending activity indicator

Related

Why is AsyncStorage not retrieving data once I refresh my App?

I am building a todo app and I am trying to store and retrieve data but it's not retrieving any data that is being stored. Once I refresh the data doesn't seem to persist. If there is another way of storing or writing my code please assist. I tried using other methods of storage like MMKV but it was just similar to AsyncStorage so I decided to stick with AsyncStorage. Here is my code:
import AsyncStorage from "#react-native-async-storage/async-storage";
export default function todaytodo() {
const [modalOpen, setModalOpen] = useState(false);
const [todos, setTodos] = useState("");
const storedata = async () => {
try {
await AsyncStorage.setItem("Todos", JSON.stringify(todos));
} catch (err) {
console.log(err);
}
};
const loadData = async () => {
try {
const value = await AsyncStorage.getItem("Todos");
if (value !== null) {
console.log(value);
return value;
}
} catch (error) {
console.log(error);
}
};
useEffect(() => {
storedata();
loadData();
});
const toggleComplete = (index) =>
setTodos(
todos.map((Todo, k) =>
k === index ? { ...Todo, complete: !Todo.complete } : Todo
)
);
const pressHandler = (key) => {
setTodos((prevTodos) => {
return prevTodos.filter((todo) => todo.key != key);
});
};
const submitHandler = (Todo) => {
Todo.key = Math.random().toString();
setTodos((currentTodo) => {
return [Todo, ...currentTodo];
});
setModalOpen(false);
};
return (
<View style={styles.container}>
<View>
<View>
<Ionicons
style={{
position: "absolute",
marginTop: 650,
alignSelf: "flex-end",
zIndex: 10,
marginRight: 5,
}}
name="md-add-circle-outline"
size={73}
color="black"
onPress={() => setModalOpen(true)}
/>
</View>
<FlatList
data={todos}
renderItem={({ item, index, complete }) => (
<TouchableOpacity onPress={() => toggleComplete(index)}>
<ScrollView>
<View style={styles.everything}>
<View style={styles.itemlist}>
<Checkbox
label="delete"
checked={true}
onPress={() => pressHandler(item.key)}
/>
<Text
style={{
marginLeft: 8,
marginTop: 5,
fontSize: 15,
textDecorationLine: item.complete
? "line-through"
: "none",
color: item.complete ? "#a9a9a9" : "black",
}}
>
{item.Todo}
</Text>
</View>
<Text
style={{
fontSize: 12,
marginLeft: 50,
marginTop: -15,
color: "#008b8b",
textDecorationLine: item.complete
? "line-through"
: "none",
color: item.complete ? "#a9a9a9" : "#008b8b",
}}
>
{item.Comment}
</Text>
</View>
</ScrollView>
</TouchableOpacity>
)}
/>
</View>
<View style={styles.modalcont}>
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
<RNModal visible={modalOpen} animationType="slide">
<View style={styles.modalContent}>
<Ionicons
name="md-close-circle-outline"
style={{ alignSelf: "center" }}
size={60}
color="black"
onPress={() => setModalOpen(false)}
/>
<AddForm submitHandler={submitHandler} />
</View>
</RNModal>
</TouchableWithoutFeedback>
</View>
</View>
);
}
Use of useEffect is suspicious here, If you want to do it once on load of component
then need to update code for useEffect.
useEffect(() => {
storedata();
loadData();
}, []);

make a vertical scroll view with horizontal rows react native

I'm new to programming and react-native and I'm trying to make a Scrollview and get values from a JSON file and show 2 boxes in each row but when I use flexDirection : 'row' they all merge into each other.
Thank you I appreciate every help.
The section below is my code:
export default class Creative extends Component {
constructor(props) {
super(props);
this.state = { isLoading: true, refreshing: false, }
}
_fetchData = () => {
const key = 'D0BEFCE031A955294DE1DD87C14B13EA77CBF8A86F293FD30E9B8D57F3C401F9';
var type = 'creative';
const lal = `imapp=${key}&type=${type}`;
fetch('https://facebook.github.io/react-native/movies.json', {
method: 'get',
headers: new Headers({
'Content-Type': 'application/x-www-form-urlencoded', // <-- Specifying the Content-Type
}),
}).then((response) => response.json())
.then((responseJson) => {
console.log(responseJson);
this.setState({
isLoading: false,
dataSource: responseJson.list,
refreshing: false,
});
})
.catch((error) => {
console.error(error);
});
}
componentDidMount() {
this._fetchData();
}
_onRefresh = () => {
() => this.setState({ refreshing: true, });
status = '1';
this._fetchData();
}
render() {
return (
<SafeAreaView>
<View style={{ backgroundColor: '#323232' }}>
<View style={{ flex: 1, paddingTop: 20 }}>
<View style={{ flex: 1, paddingTop: 20 }}>
<FlatList
data={this.state.dataSource}
renderItem={({ item }) => <Text>{item.title}, {item.releaseYear}</Text>}
keyExtractor={({ id }, index) => id}
/>
</View>
</View>
</View>
</SafeAreaView>
);
}
}
Use numColumns in ReactNative Flatlist to render multiple columns & Change your Flatlist as below
<FlatList
data={this.state.dataSource}
renderItem={({ item }) => (
<View style={{ width: '40%', justifyContent: 'space-between', backgroundColor: 'red', margin: 5 }}>
<Text>{item.title}, {item.releaseYear}</Text>
</View>
)}
keyExtractor={item => item.id}
numColumns={2}
/>
Change this according to your requirements.
Hope this helps you. Feel free for doubts.

Use global variable to generate a dynamic url (React Native)

I have a datapicker where I select the country and with this I create a url.
<View style={styles.centrado}>
<Text style={styles.seleccion}>Elige tu País</Text>
{this.Paises()}
<Picker
selectedValue={this.state.value || ""}
style={{ height: 50, width: 120 }}
itemStyle={styles.seleccion}
onValueChange={(value, idx) => {
this.setState({ value, idx });
global.Pais = value;
console.log({ valor: value });
this.props.navigation.navigate("Noticias", {
pais: value
});
}}
>
<Picker.Item label="Seleccione" value="" />
<Picker.Item label="Argentina" value="ar" />
<Picker.Item label="Bolivia" value="bo" />
</Picker>
</View>
In the first load everything works but when I return to the home and select another country the global (global.Pais) variable remains with the initial value.
export default class noticias extends React.Component {
state = {
loading: true,
noticias: []
};
constructor(props) {
super(props);
this.fetchNoticias();
}
fetchNoticias = async () => {
console.log(global.Pais);
if (!global.Pais || global.Pais == "undefined") {
alert("Selecciona un país para ver las noticias");
this.props.navigation.navigate("Home");
} else if (global.Pais == "uy") {
const response = await fetch(
`https://prueba.${global.Pais}/wp-json/wp/v2/posts`
);
const res = await response.json();
this.setState({ noticias: res, loading: false });
} else {
const response = await fetch(
`https://prueba.com.${global.Pais}/wp-json/wp/v2/posts`
);
const res = await response.json();
this.setState({ noticias: res, loading: false });
}
};
componentDidMount() {
this.fetchNoticias();
}
render() {
const { loading, noticias } = this.state;
if (loading) {
return (
<View style={styles.container}>
<Text>Cargando .....</Text>
</View>
);
}
return (
<View>
<FlatList
data={this.state.noticias}
keyExtractor={(x, i) => i.toString()}
renderItem={({ item }) => (
<View>
<TouchableOpacity
onPress={() =>
this.props.navigation.navigate("Noticia", {
post_id: item.id
})
}
>
<Card
style={{
shadowOffset: { width: 5, height: 5 },
width: "90%",
borderRadius: 12,
alignSelf: "center",
marginBottom: 10
}}
>
<Card.Content>
<Title>{item.title.rendered}</Title>
</Card.Content>
<Card.Cover
source={{
uri:
item.better_featured_image.media_details.sizes.medium
.source_url
}}
/>
<Card.Content>
<HTMLRender html={item.excerpt.rendered} />
</Card.Content>
</Card>
</TouchableOpacity>
</View>
)}
/>
</View>
);
}
}
How can I solve it?
I think that won't work, you could use react-context or redux to save and update that value or
this.props.navigation.setParams({ pais: value })
and then get that value when you need it
this.props.navigation.getParam('pais')

Flatlist is not reading "data" variable set using hooks in react native

I'm using hooks in react native project and setting data variable with referenced firebase database, when I'm console logging this data variable, It is showing null at first and within few seconds it shows fetched data, but flatlist is not reading it. Please correct my mistake. Here is my code:
RecommenTab = () => {
useFirebaseQuery = ref => {
const [docState, setDocState] = useState({
isLoading: true,
data: null
});
useEffect(() => {
return ref.on("value", snapshot => {
setDocState({
isLoading: false,
data: snapshot
});
});
}, []);
return docState;
};
renderSeparator = () => {
return (
<View
style={{
height: 1,
width: "86%",
backgroundColor: "#CED0CE",
marginLeft: "14%"
}}
/>
);
};
userList = () => {
const ref = database.ref("users");
const { isLoading, data } = this.useFirebaseQuery(ref);
if (data != []) {
return (
<SafeAreaView style={{ marginTop: 20 }}>
{console.log(data)}
<Text>SHOW</Text>
<FlatList
data={data}
renderItem={({ item }) => (
<View>
<Text>Work</Text>
<ListItem
title={item.name}
subtitle={item.username}
leftAvatar={{
rounded: true,
source: { uri: item.avatar }
}}
/>
</View>
)}
keyExtractor={item => item.id}
ItemSeparatorComponent={this.renderSeparator}
/>
</SafeAreaView>
);
}
};
return <View>{this.userList()}</View>;
};

Using parameter that would fetch data from another api based on a condition react native?

I have two pages Portfolio.js and PortfolioDetails.js. In the Portfolio.js file, I am fetching data from my api and displaying all the portfolios in a list. When I click on portfolio, it should take me to the PortfolioDetails screen, which will display only those stocks from stock api which are in the portfolio.
e.g if I click on Portfolio with id 1, it should filter out stocks from stock api which has portfolio id 1 and display all those stocks on the screen.
So far, I am successful in fetching both the apis and also when I click on one portfolio, it passes the portfolio id parameter to my PortfolioDetails screen. I am stuck where I have to filter the stocks to display based on this passed parameter - id.
Portfolio.js file
export default class Portfolio extends React.Component {
static navigationOptions = ({ navigation }) => {
return {
title: "Portfolio",
header: null,
};
};
constructor(props) {
super(props);
this.state = {
loading: true,
PortfolioSource: []
};
}
componentDidMount() {
fetch("http://127.0.0.1:8000/portfolios/")
.then(response => response.json())
.then((responseJson) => {
this.setState({
loading: false,
PortfolioSource: responseJson
})
})
.catch(error => console.log(error)) //to catch the errors if any
}
FlatListItemSeparator = () => {
return (
<View style={{
height: .5,
width: "100%",
backgroundColor: "rgba(0,0,0,0.5)",
}}
/>
);
}
renderItem = (data) =>
<TouchableOpacity style={styles.list} onPress={() => this.props.navigation.push('Details', { portid: data.item.id })} >
<Text style={styles.lightText}>{data.item.id}</Text>
<Text style={styles.lightText}>{data.item.portfolio_id}</Text>
<Text style={styles.lightText}>{data.item.name}</Text>
<Text style={styles.lightText}>{data.item.description}</Text>
<Text style={styles.lightText}>{data.item.gains}</Text></TouchableOpacity>
render() {
if (this.state.loading) {
return (
<View style={styles.loader}>
<ActivityIndicator size="large" color="#0c9" />
</View>
)
}
return (
<View style={styles.container}>
<FlatList
data={this.state.PortfolioSource}
ItemSeparatorComponent={this.FlatListItemSeparator}
renderItem={item => this.renderItem(item)}
keyExtractor={item => item.id.toString()}
/>
</View>
)
}
}
PortfolioDetails.js
export default class PortfolioDetails extends React.Component {
static navigationOptions = ({ navigation }) => {
return {
title: "PortfolioDetails",
header: null,
};
};
constructor(props) {
super(props);
this.state = {
loading: true,
PortfolioDetailsdataSource: [],
};
}
componentDidMount() {
fetch(`http://127.0.0.1:8000/stocks/`)
.then(response => response.json())
.then((responseJson) => {
this.setState({
loading: false,
PortfolioDetailsdataSource: responseJson
})
})
.catch(error => console.log(error)) //to catch the errors if any
}
FlatListItemSeparator = () => {
return (
<View style={{
height: .5,
width: "100%",
backgroundColor: "rgba(0,0,0,0.5)",
}}
/>
);
}
goToPrevScreen = () => {
this.props.navigation.goBack();
}
renderItem = (data) =>
<TouchableOpacity style={styles.list}>
<Text style={styles.lightText}>{data.item.id}</Text>
<Text style={styles.lightText}>{data.item.ticker}</Text>
<Text style={styles.lightText}>{data.item.price}</Text>
<Text style={styles.lightText}>{data.item.market_cap}</Text>
<Text style={styles.lightText}>{data.item.YTD}</Text>
<Text style={styles.lightText}>{data.item.OneYear}</Text>
<Text style={styles.lightText}>{data.item.TwoYear}</Text>
<Text style={styles.lightText}>{data.item.TTM_Sales_Growth}</Text>
<Text style={styles.lightText}>{data.item.PE_Ratio}</Text>
</TouchableOpacity>
render() {
if (this.state.loading) {
return (
<View style={styles.loader}>
<ActivityIndicator size="large" color="#0c9" />
</View>
)
}
return (
<View style={styles.container}>
<FlatList
data={this.state.PortfolioDetailsdataSource}
ItemSeparatorComponent={this.FlatListItemSeparator}
renderItem={item => this.renderItem(item)}
keyExtractor={item => item.id.toString()}
/>
<Text> portid: {this.props.navigation.state.params.portid} </Text>
<Button
onPress={() => this.goToPrevScreen()}
title="go back to Portfolio"
/>
</View>
)
}
}
You can use .find(). For example:
PortfolioDetailsDataSource.find(item => item.id === this.props.navigation.state.params.portId)
Assuming IDs are unique, this will return the desired object, otherwise it will return the first occurrence that passes the condition.