Use global variable to generate a dynamic url (React Native) - 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')

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();
}, []);

Searching in Expo's FlatList

I used this tutorial but it didn't work (if you are interested, check out my last post). Any suggestions to make a working search for a flatlist?
I have a list of 100 things and just by inserting the name in a search bar, the flatlist should update with the results.
Try using react-native-searchable-flatlist
import React, { Component } from 'react';
import { View, Text, FlatList, ActivityIndicator } from 'react-native';
import { ListItem, SearchBar } from 'react-native-elements';
class FlatListDemo extends Component {
constructor(props) {
super(props);
this.state = {
loading: false,
data: [],
error: null,
};
this.arrayholder = [];
}
componentDidMount() {
this.makeRemoteRequest();
}
makeRemoteRequest = () => {
const url = `https://randomuser.me/api/?&results=20`;
this.setState({ loading: true });
fetch(url)
.then(res => res.json())
.then(res => {
this.setState({
data: res.results,
error: res.error || null,
loading: false,
});
this.arrayholder = res.results;
})
.catch(error => {
this.setState({ error, loading: false });
});
};
renderSeparator = () => {
return (
<View
style={{
height: 1,
width: '86%',
backgroundColor: '#CED0CE',
marginLeft: '14%',
}}
/>
);
};
searchFilterFunction = text => {
this.setState({
value: text,
});
const newData = this.arrayholder.filter(item => {
const itemData = `${item.name.title.toUpperCase()} ${item.name.first.toUpperCase()} ${item.name.last.toUpperCase()}`;
const textData = text.toUpperCase();
return itemData.indexOf(textData) > -1;
});
this.setState({
data: newData,
});
};
renderHeader = () => {
return (
<SearchBar
placeholder="Type Here..."
lightTheme
round
onChangeText={text => this.searchFilterFunction(text)}
autoCorrect={false}
value={this.state.value}
/>
);
};
render() {
if (this.state.loading) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<ActivityIndicator />
</View>
);
}
return (
<View style={{ flex: 1 }}>
<FlatList
data={this.state.data}
renderItem={({ item }) => (
<ListItem
leftAvatar={{ source: { uri: item.picture.thumbnail } }}
title={`${item.name.first} ${item.name.last}`}
subtitle={item.email}
/>
)}
keyExtractor={item => item.email}
ItemSeparatorComponent={this.renderSeparator}
ListHeaderComponent={this.renderHeader}
/>
</View>
);
}
}
export default FlatListDemo;

I have an array list saved in asyncstorage with key 'list' My goal is that i need them to be rendered as picker. Items How can i do this?

export default class Incomel extends Component {
state = {
isEdit: null,
list: [],
isLoading: false,
editText: '',
};
componentDidMount = () => {
this.setState({ isLoading: true });
// AsyncStorage.removeItem('list')
AsyncStorage.getItem('list')
.then(list => {
if (list) {
this.setState({ list: JSON.parse(list), isLoading: false });
} else {
this.setState({ list: [], isLoading: false });
}
})
.catch(err => {
this.setState({ isLoading: false });
});
};
add = () => {
let list = this.state.list;
list.push('');
this.setState({ list: list });
this.saveToStorage();
this.setEdit(list.length - 1);
};
setEdit = index => {
if (this.state.isEdit !== index) {
this.setState({ isEdit: index, editText: this.state.list[index] });
}
};
setList = (text, index) => {
let list = this.state.list;
list[index] = text;
this.setState({ list: list, isEdit: null, editText: '' });
this.saveToStorage();
};
saveToStorage = () => {
let data = JSON.stringify(this.state.list);
AsyncStorage.setItem('list', data);
};
deleteItem = index => {
let list = this.state.list;
list.splice(index, 1);
this.setState({ list: list });
this.saveToStorage();
};
render() {
return (
<ScrollView style={style.container}>
<View style={style.header}>
<Text style={style.headerText}>Incomes</Text>
<Image style={{ width: 50, height: 50 }} source={require('../android/assets/1322767.png')} />
</View>
{this.state.isLoading ? (
<ActivityIndicator color="#d28888" size="large" />
) : (
<View style={style.body}>
{this.state.list.map((item, key) => (
<React.Fragment>
{this.state.isEdit === null || this.state.isEdit !== key ? (
<TouchableOpacity
style={style.item}
activeOpacity={0.5}
onLongPress={() => this.setEdit(key)}>
<Text style={style.itemText}>{item}</Text>
<TouchableOpacity
style={style.itemDelete}
onPress={() => this.deleteItem(key)}>
<Image style={{ width: 30, height: 30 }} source={require('../android/assets/delete-icon.png')} />
</TouchableOpacity>
</TouchableOpacity>
) : null}
{this.state.isEdit !== null ? (
key == this.state.isEdit ? (
<TextInput
style={style.itemInput}
onBlur={() => this.setList(this.state.editText, key)}
onSubmitEditing={() =>
this.setList(this.state.editText, key)
}
value={this.state.editText}
autoFocus
onChangeText={editText => this.setState({ editText })}
/>
) : null
) : null}
</React.Fragment>
))}
<TouchableOpacity style={style.btnAdd} onPress={() => this.add()}>
<Image style={{ width: 50, height: 50 }} source={require('../android/assets/add-icon.png')} />
</TouchableOpacity>
</View>
)}
</ScrollView>
);
}
}

using classes and rendering api call in React Native

I am totally new in React Native, I am having problem rendering data from API call. When I do it inside function it is working for me when I am using useEffect... but in the Class I cannot use that.
Here is example of my code...
export default class Categories extends React.Component {
constructor(props) {
super(props);
this.state = {
loading: false,
data: [],
error: null,
};
}
componentDidMount() {
this.regionsGetRequest();
}
regionsGetRequest = () => {
this.setState({ loading: true });
const fetchData = async () => {
const result = await axios(
'https://...........json'
);
this.setState({
data: result.data,
error: result.error || null,
loading: false,
});
};
fetchData();
};
renderCategories = () => {
const categoryLive = this.state.data;
console.log(JSON.stringify(categoryLive));
I am getting in console: undefined, undefined... and then results as they should... like it is running 3 times for some reason... if I try to put above renderCategories:
componentDidMount() {
renderCategories();
}
I am getting just one undefined... when I connect variable categoryLive nothing is loading.
Sorry I have been strugling with this one... any help is really appreciated!!
No matter what I do, I am always getting 3 calls... first two empty object [], and third I get real results dumped in console. So my categories are not rendering.. they are empty.
Here is updated code, and I am posting whole file, it might ring some bells.
export default class Categories extends React.Component {
state = {
myData: [],
};
componentDidMount() {
axios
.get('https://..............json')
.then((res) => {
const myData = res.data;
this.setState({ myData });
});
}
renderCategories = () => {
const categoryLive = this.state.myData;
console.log(JSON.stringify(categoryLive));
const { navigation, route } = this.props;
const tabId = route.params?.tabId;
const categories = tabId
? categoryLive[tabId]
: categoryLive.victoria_bc;
//console.log(route.params?.tabId);
return (
<ScrollView
showsVerticalScrollIndicator={false}
contentContainerStyle={styles.categoryList}
>
<Block flex>
{categories.map((category) => (
<TouchableWithoutFeedback
key={`category-${category.id}`}
onPress={() => navigation.navigate('Category', { ...category })}
>
<Block flex card style={[styles.category, styles.shadow]}>
<ImageBackground
source={{ uri: category.image }}
style={[
styles.imageBlock,
{ width: width - theme.SIZES.BASE * 2, height: 252 },
]}
imageStyle={{
width: width - theme.SIZES.BASE * 2,
height: 252,
}}
>
<Block style={styles.categoryTitle}>
<Text size={18} bold color={theme.COLORS.WHITE}>
{category.title}
</Text>
</Block>
</ImageBackground>
</Block>
</TouchableWithoutFeedback>
))}
</Block>
</ScrollView>
);
};
render() {
return (
<Block flex center style={styles.categories}>
{this.renderCategories()}
</Block>
);
}
}
When I put it like this: I am getting default category ( and all data just fine... ) but my navigation is not working any more... (route.params?.tabId is not updating)
axios
.get('https://.............json')
.then((res) => {
this.setState({
myData: res.data,
error: res.error || null,
loading: false,
});
console.log('inside .then----' + JSON.stringify(this.state.myData));
const { navigation, route } = this.props;
const tabId = route.params?.tabId;
const tmpCategories = tabId
? this.state.myData[tabId]
: this.state.myData.victoria_bc;
this.setState({ categories: tmpCategories });
//console.log(route.params?.tabId);
});
If I put it like this as below... category is empty for me:
axios
.get('https://.............json')
.then((res) => {
this.setState({
myData: res.data,
error: res.error || null,
loading: false,
});
console.log('inside .then----' + JSON.stringify(this.state.myData));
});
const { navigation, route } = this.props;
const tabId = route.params?.tabId;
const tmpCategories = tabId
? this.state.myData[tabId]
: this.state.myData.victoria_bc;
this.setState({ categories: tmpCategories });
//console.log(route.params?.tabId);
Final code that is working for me..
export default class Categories extends React.Component {
constructor(props) {
super(props);
this.state = {
loading: true,
myData: [],
error: null,
};
}
componentDidMount() {
this.renderCategories();
}
renderCategories = () => {
axios
.get('https://.............json')
.then((res) => {
this.setState({
myData: res.data,
error: res.error || null,
loading: false,
});
//console.log('inside .then----' + JSON.stringify(this.state.myData));
});
};
render() {
if (this.state.loading) {
return (
<View
style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}
>
<ActivityIndicator />
</View>
);
} else {
const { navigation, route } = this.props;
const tabId = route.params?.tabId;
const categories = tabId
? this.state.myData[tabId]
: this.state.myData.victoria_bc;
//console.log(route.params?.tabId);
return (
<Block flex center style={styles.categories}>
<ScrollView
showsVerticalScrollIndicator={false}
contentContainerStyle={styles.categoryList}
>
<Block flex>
{categories.map((category) => (
<TouchableWithoutFeedback
key={`category-${category.id}`}
onPress={() =>
navigation.navigate('Category', { ...category })
}
>
<Block flex card style={[styles.category, styles.shadow]}>
<ImageBackground
source={{ uri: category.image }}
style={[
styles.imageBlock,
{ width: width - theme.SIZES.BASE * 2, height: 252 },
]}
imageStyle={{
width: width - theme.SIZES.BASE * 2,
height: 252,
}}
>
<Block style={styles.categoryTitle}>
<Text size={18} bold color={theme.COLORS.WHITE}>
{category.title}
</Text>
</Block>
</ImageBackground>
</Block>
</TouchableWithoutFeedback>
))}
</Block>
</ScrollView>
</Block>
);
}
}
}
export default class Categories extends React.Component {
constructor(props) {
super(props);
this.state = {
loading: false,
myData: [],
error: null,
categories: []
};
}
componentDidMount() {
renderCategories();
}
renderCategories = () => {
this.setState({ loading: true });
axios
.get('https://..............json')
.then((res) => {
this.setState({ myData: res.data,
error: res.error || null,
loading: false
});
const categoryLive = this.state.myData;
console.log(JSON.stringify(categoryLive));
});
};
render() {
// Set params in your render method :
const { navigation, route } = this.props;
const tabId = route.params?.tabId;
if (this.state.loading){
return (
<View style={{ flex : 1, alignItems: 'center', justifyContent: 'center' }}>
<ActivityIndicator/>
</View>
);
}
return (
// assign in your return statement
this.setState({ categories: tabId
? categoryLive[tabId]
: categoryLive.victoria_bc;})
//console.log(route.params?.tabId);
const { categories } = this.state.categories;
<Block flex center style={styles.categories}>
<ScrollView
showsVerticalScrollIndicator={false}
contentContainerStyle={styles.categoryList}
>
<Block flex>
{categories.map((category) => (
<TouchableWithoutFeedback
key={`category-${category.id}`}
onPress={() => navigation.navigate('Category', { ...category })}
>
<Block flex card style={[styles.category, styles.shadow]}>
<ImageBackground
source={{ uri: category.image }}
style={[
styles.imageBlock,
{ width: width - theme.SIZES.BASE * 2, height: 252 },
]}
imageStyle={{
width: width - theme.SIZES.BASE * 2,
height: 252,
}}
>
<Block style={styles.categoryTitle}>
<Text size={18} bold color={theme.COLORS.WHITE}>
{category.title}
</Text>
</Block>
</ImageBackground>
</Block>
</TouchableWithoutFeedback>
))}
</Block>
</ScrollView>
</Block>
);
}
}

stop activity indicator when all data has been fetched from server

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