too many rerenders on using useState - javascript

I am calling the contactsfunction from the main return. I don't see any error until here:
const showContacts = React.useCallback(
(data: UsersQueryHookResult) => {
if (data) {
return (
<View style={styles.users}>
</View>
);
}
},
[userData],
);
const contacts = () => {
console.log('running');
const { loading, error, data } = useUsersQuery({
variables: {
where: { id: 34 },
},
});
console.log('DATA COMING', data);
//setUserData(data);
//console.log('contact name', data.users.nodes[0].userRelations[0].relatedUser.firstName);
};
return (
<SafeAreaView style={{ flex: 1 }}>
<Container style={{ flex: 1, alignItems: 'center' }}>
<Item style={styles.addToWhitelist}>
<Icon
name="add"
onPress={() => navigation.navigate('AddContactTry')}
/>
<Text style={styles.addToContactTitle}>Add contact</Text>
</Item>
<Text onPress={() => navigation.navigate('Home')}>Zurück</Text>
<View style={{ width: moderateScale(350) }}>
<Text>Keine Kontacte</Text>
</View>
{contacts()}
</Container>
</SafeAreaView>
);
};
Now I want to do some conditional rendering on the basis of the results from contacts. However, as soon as I uncomment setUserData(data); I get an error that too many re-renders. I don't get what I am doing wrong in showUsersto get this error.
Edit:
I tried this but it gives an invalid hook call error:
export const Whitelist: React.FunctionComponent = (props) => {
const [userData, setUserData] = useState<UsersQueryHookResult>('');
useEffect(() => {
// Your function to fetch/get contacts
const data = contacts();
setUserData(data);
}, [])
const showContacts = React.useCallback(
(data: UsersQueryHookResult) => {
if (data) {
return (
<View style={styles.users}>
</View>
);
}
},
[userData],
);
const contacts = () => {
console.log('running');
const { loading, error, data } = useUsersQuery({
variables: {
where: { id: 34 },
},
});
console.log('DATA COMING', data);
//setUserData(data);
};
return (
<SafeAreaView style={{ flex: 1 }}>
<Container style={{ flex: 1, alignItems: 'center' }}>
<Item style={styles.addToWhitelist}>
<Icon
name="add"
onPress={() => navigation.navigate('AddContactTry')}
/>
<Text style={styles.addToContactTitle}>Add contact</Text>
</Item>
<Text onPress={() => navigation.navigate('Home')}>Zurück</Text>
{/* {contacts()} */}
</Container>
</SafeAreaView>
);
};

Try to fetch contacts in componentDidMount method or useEffect (in your case with hooks) and store the result in a state with useState.
const [contacts, setContacts] = React.useState(null);
[...]
React.useEffect(() => {
// Your function to fetch/get contacts
const data = contacts();
setContacts(data);
}, [])
[...]
return (
<SafeAreaView style={{ flex: 1 }}>
[...]
{contacts && contacts.map(contact => (<View>{contact.name}</View>)}
[...]
</SafeAreaView>
);
hope it helps.

Related

Cannot read property of 'navigate' of undefined

I am trying to make a stack navigator using reactstack navigation. When the button clicks, it appears on the detail screen only, the title of the page is detail. I am not yet to parsing data array to the next screen, it just tries to navigate the screen into the detail screen, and gets this error. I am new to react. Please help me solve this problem.
import React from 'react'
import {Button, StyleSheet, ScrollView, Text, View} from 'react-native'
import {useState, useEffect} from 'react'
import axios from 'axios'
const Item = ({id, user_id, title, onPress, navigation}) => {
return (
<View style={styles.container}>
<Text style={styles.text}>Id :{id}
</Text>
<Text style={styles.text}>User Id :{user_id}
</Text>
<Text style={styles.text}>Tittle :{title}
</Text>
<View style={styles.container}>
<Button onPress={() => navigation.navigate('Detail')} title='Detail'></Button>
</View>
<View style={styles.line}></View>
</View>
)
}
const Berita = () => {
const [users,
setUsers] = useState([]);
useEffect(() => {
getData();
}, []);
const selectItem = (item) => {
console.log('Selected item: ', item)
}
const getData = () => {
axios
.get('https://gorest.co.in/public/v1/posts')
.then(res => {
console.log('res: ', res);
setUsers(res.data.data);
})
}
return (
<ScrollView style={styles.container}>
{users.map(user => {
return <Item key={user.id} id={user.id} user_id={user.user_id} title={user.title}/>
})}
</ScrollView>
)
}
export default Berita
const styles = StyleSheet.create({
container: {
padding: 15
},
text: {
color: "black",
marginTop: 5,
fontStyle: 'italic',
fontSize: 18,
fontFamily: 'Arial'
},
line: {
height: 1,
backgroundColor: 'black',
marginVertical: 20
},
title: {
fontSize: 25,
fontWeight: 'bold',
textAlign: 'center',
color: "black"
},
tombol: {
padding: 10
}
})
This is the stack screen navigator code
const Tab = createBottomTabNavigator();
const Stack = createStackNavigator();
const DetailBerita = () => {
return (
<Stack.Navigator >
<Stack.Screen
name='Berita'
component={Berita}
options={{
headerTitleAlign: 'center'
}}/>
<Stack.Screen
name="Detail"
component={Detail}
options={{
headerTitleAlign: 'center'
}}/>
</Stack.Navigator>
)
}
It appears that you are using Stack Navigators with different screen names, but you didn't send it. If possible, can you send that file, I would be able to help you a bit better. But from what I have I can try and explain how Stack navigation works. With this line of code:
navigation.navigate('Detail')
You specify that you want to navigate to the screen named "Detail", if you want to navigate to another screen then you can change it to the name of the screen. Let's say you want to navigate to your home screen, and its named "Home" in your Stack.Navigation component. Simply change your navigation to the following:
navigation.navigate('Home')
This is happening because the navigation prop is passed to the Berita component and you are destructuring the property in Item component not in Berita.
So the code should look like
...
const Berita = ({ navigation }) => {
// ...
return (
<ScrollView style={styles.container}>
{users.map(user => {
return (
<Item
key={user.id}
id={user.id}
user_id={user.user_id}
title={user.title}
navigation={navigation} // Pass navigation
/>
);
})}
</ScrollView>
);
};
Another way is - you can just use navigation in onPress (Berita) and pass down onPress to Item component
const Item = ({ id, user_id, title, onPress }) => {
return (
<View style={styles.container}>
<Text style={styles.text}>Id :{id}</Text>
<Text style={styles.text}>User Id :{user_id}</Text>
<Text style={styles.text}>Tittle :{title}</Text>
<View style={styles.container}>
<Button onPress={onPress} title="Detail" />
</View>
<View style={styles.line}></View>
</View>
);
};
const Berita = ({ navigation }) => {
const [users, setUsers] = useState([]);
useEffect(() => {
getData();
}, []);
const selectItem = item => {
console.log('Selected item: ', item);
};
const getData = () => {
axios.get('https://gorest.co.in/public/v1/posts').then(res => {
console.log('res: ', res);
setUsers(res.data.data);
});
};
return (
<ScrollView style={styles.container}>
{users.map(user => {
return (
<Item
key={user.id}
id={user.id}
user_id={user.user_id}
title={user.title}
onPress={() => navigation.navigate('Detail')}
/>
);
})}
</ScrollView>
);
};

React native shared elements won't work with react navigation 4

I'm trying to implement react-navigation-shared-elements with react-navigation v4, but somthing wrong with the way I'm trying.
I have Screen , that contain component ( SectionList) that rendering and other component ( TaskCard ) that contain the wanted shared element.
I've tried to implement this according to the documentation, but it's not working.
Please see the code:
My Router:
const HomeStack = createSharedElementStackNavigator({
HomeScreen,
TaskDetailsScreen,
EditTaskScreen,
}, {
defaultNavigationOptions: headerDefaultOption,
});
const AppNavigator = createSwitchNavigator(
{
SplashScreen,
AuthStack,
HomeStack,
}
);
HomeScreen that contain the SectionList component:
export const HomeScreen = ({navigation}) => {
const onTaskPressHandler = (task) => {
navigation.push(screens.TASK_DETAILS_SCREEN, {task});
};
return (
<View style={styles.container}>
<TaskList data={tasks} onTaskPress={onTaskPressHandler} onTaskLongPress={openQuickActionsWithData}/>
</View>
);
};
TaskList:
export const TaskList = ({data, onTaskPress, onTaskLongPress}) => {
data.sort((a, b) => (a.taskEndDate > b.taskEndDate) ? 1 : ((b.taskEndDate > a.taskEndDate) ? -1 : 0));
return (
<SectionList
showsVerticalScrollIndicator={false}
sections={sortedData}
keyExtractor={(item, index) => item + index}
renderItem={({item, index}) => {
return (
<TaskCard data={item} index={index} onTaskPress={onTaskPress} onTaskLongPress={onTaskLongPress}/>
);
}}
renderSectionHeader={({section: {title}, section: {data}}) => {
return (
renderTitleIfHasData(data, title)
);
}}
/>
);
};
TaskCard with shared element :
import {SharedElement} from 'react-native-shared-element';
export const TaskCard = ({data, index, onTaskPress, onTaskLongPress}) => {
const onPressHandler = data => {
onTaskPress(data)
};
return (
<Animated.View rkey={index} style={[styles.mainContainer, {transform: [{scale: scaleAnim}]}]}>
<TouchableOpacity
index={index}
activeOpacity={layout.activeOpacity}
style={[styles.taskContainer, {backgroundColor: isFinished ? color.ORANGE : color.GREY,}, renderBorderRadiusPosition()]}
onPress={() => onPressHandler(data)}
onLongPress={() => onTaskLongPress(data)}>
<View style={styles.titleContainer}>
<SharedElement id={`data.${data.taskCreationDate}.sharedTask`}>
<View style={styles.taskImageTypeContainer}>
<Image source={getTaskImageByType(data.taskType)} style={styles.typeImageStyle}/>
</View>
</SharedElement>
</TouchableOpacity>
</Animated.View>
);
};
Details screen with same shared element :
export const TaskDetailsScreen = ({navigation}) => {
return (
<ScrollView style={styles.mainContainer}>
<View style={{flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center'}}>
<SharedElement id={`data.${task.taskCreationDate}.sharedTask`}>
<View style={styles.taskTypeContainer}>
<Image source={getTaskImageByType(task.taskType)} style={styles.taskTypeImage}/>
</View>
</SharedElement>
</View>
</ScrollView>
);
};
TaskDetailsScreen.sharedElements = (navigation, otherNavigation, showing) => {
const task = navigation.getParam('task');
return [{
id: `data.${task.taskCreationDate}.sharedTask`,
animation: 'move',
resize: 'clip'
}];
};

filter elements in a array in async function

im trying to filter my api array in this just for the users witch speciality == Dermatologista, can you help me?
export default class Dermatologistas extends Component{
state ={
errorMessage: null,
users: []
}
here I create the async function that is getting the users
getUserList = async () => {
try {
const response = await api.get('/auth/list');
const { users } = response.data
console.log(response.data)
this.setState({ users });
} catch (response) {
this.setState({ errorMessage: response.data.error });
}
};
componentDidMount() {
this.getUserList()
}
here it renders all users and not filter, how can I do this ?
render(){
const users = this.state.users
console.log(users)
return(
<View >
{ this.state.errorMessage && <Text>{ this.state.errorMessage }</Text> }
{this.state.users.map(user => (
<View key={user._id} style={{marginTop: 15, alignItems: 'center'}}>
<Text>{user.title}</Text>
<Text>{user.speciality}</Text>
<Button title = 'View Profile'onPress ={() =>
this.props.navigation.navigate('Profile')}/>
</View>
))}
</View>
)
}
}
You can create a function which takes the data that's being received along with the parameter through which you want to filter the data.
const filterBySpeciality = (data, speciality) => data.filter(d => d.speciality == speciality)
let data = [{speciality: "Ortho", title: "Chief Surgeon"}, {speciality: "Dermatologista", title: "Specialist"}];
console.log(filterBySpeciality(data, "Dermatologista"));
So, in your case you can call filterBySpeciality(data, "Dermatologista") in your render function and use the result for populating the View like below.
const filterBySpeciality = (data, speciality) => data.filter(d => d.speciality == speciality)
render() {
const filteredData = this.filterBySpeciality(this.state.users, "Dermatologista");
return(
<View >
{ this.state.errorMessage && <Text>{ this.state.errorMessage }</Text> }
{/* Here I have changed this.state.users to filteredData */}
{filteredData.map(user => (
<View key={user._id} style={{marginTop: 15, alignItems: 'center'}}>
<Text>{user.title}</Text>
<Text>{user.speciality}</Text>
<Button title = 'View Profile'onPress ={() =>
this.props.navigation.navigate('Profile')}/>
</View>
))}
</View>
)
}
Just put condition before rendering to data.
getUserList = async () => {
try {
const response = await api.get('/auth/list');
const usersData = response.data
const users = usersData.filter((user: any) => {
if (user.speciality === 'Dermatologista') return user;
})
console.log(users);
this.setState({ users });
} catch (response) {
this.setState({ errorMessage: response.data.error });
}
};
componentDidMount() {
this.getUserList()
}
Or you can try something like this :
render(){
const usersData = this.state.users
const users = usersData.filter((user: any) => {
if (user.speciality === 'Dermatologista') return user;
})
console.log(users)
return(
<View >
{ this.state.errorMessage && <Text>{ this.state.errorMessage }</Text> }
{this.state.users.map(user => (
<View key={user._id} style={{marginTop: 15, alignItems: 'center'}}>
<Text>{user.title}</Text>
<Text>{user.speciality}</Text>
<Button title = 'View Profile'onPress ={() =>
this.props.navigation.navigate('Profile')}/>
</View>
))}
</View>
)
}
}
in the view just this:
<View >
{ this.state.errorMessage && <Text>{ this.state.errorMessage }</Text> }
{this.state.users.filter(user => user.speciality === 'Dermatologista').map(user => (
<View key={user._id} style={{marginTop: 15, alignItems: 'center'}}>
<Text style={{fontWeight: 'bold', fontSize:20}}>{user.title}</Text>
<Text>{user.speciality}</Text>
<Button title = 'View Profile'onPress ={() => this.props.navigation.navigate('Profile')}/>
</View>

react-native how do I show activityindicator while file uploading

I want to have a loading spinner appear in the middle of the screen while the file is being uploaded.
The clicked file is loaded and appears in the file list as well.
I imported ActivityIndicator from 'react-native'.
When I wrote the code after as a test, the loading spinner was well seen.
However, I wanted to make the spinner appear only the file is loaded. So, I wrote the code, but the loading spinner does not appear.
import {Text,View,ImageBackground,Image,StyleSheet,TouchableOpacity,FlatList,Button,ActivityIndicator} from 'react-native';
export default function FileUpload({route, navigation}) {
const [multipleFile, setMF] = useState([])
const [isLoading, setLoading] = useState(true);
useEffect(() => {
setTimeout(() => {
setLoading(false);
}, 5000);
}, [])
const addEntry = (data) => {
let newObj = {
"id": randId(),
"name" : data.filename,
"text" : data.text,
}
console.log('Finish load');
setMF([...multipleFile, newObj]);
console.log(multipleFile)
};
const randId = () => {
let id = ''
for (var i = 0; i < 10; i++) {
id += String.fromCharCode(Math.trunc(Math.random()*85) + 48)
}
return id
}
const sendFile = () => {
axios({
method: 'post',
url: '',
data: {
"uri": ""
}
}).then((res) =>{
addEntry(res.data)
}
).catch((err) => console.log(err));
}
const renderItem = ({ item }) => (
<Text>{item.name}</Text>
);
return (
<ImageBackground source={require('../images/bg2.png')}
style={{width: '100%', height: '100%'}}>
<View>
<TouchableOpacity onPress={() => {
DocumentPicker.getDocumentAsync().then((res) => {
console.log(res)
if (res.type == 'success') {
console.log('load start');
if(isLoading){
return(
<View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
<ActivityIndicator size="large"/>
</View>
);
}
sendFile()
} else {
console.log('you cancelled')
}
})
}}
style={{alignItems: 'center'}}>
<Image source ={require('../images/cloud.png')}
style={styles.image}
/>
<View style={styles.viewTextStyle}>
<Text style={styles.textStyle}>{'Upload Files'} </Text>
<Text style={{fontSize:60, color:'white'}}> + </Text>
<Text style={styles.textStyle1}>{'Browse and select your file \n you want to upload'}</Text>
</View>
</TouchableOpacity>
</View>
<View style={styles.listHeader}>
<Text style={{textAlign: 'center', fontSize: 18}}> File List </Text>
</View>
<FlatList
style={styles.scrollContainer}
data={multipleFile}
renderItem={renderItem}
keyExtractor={item => item.id.toString()}>
</FlatList>
{/* <View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
<ActivityIndicator size="large"/>
</View> */}
<TouchableOpacity onPress={() => {
// console.log(multipleFile[0].name)
navigation.navigate('Details Search',
multipleFile[0]
)}}>
<View style={styles.button}>
<Text style={styles.buttonText}>Next</Text>
</View>
</TouchableOpacity>
</ImageBackground>
);
}
set the default state to false
const [isLoading, setLoading] = useState(false);
remove this effect
useEffect(() => {
setTimeout(() => {
setLoading(false);
}, 5000);
}, [])
remove loading view from DocumentPicker.getDocumentAsync()
if (res.type == 'success') {
sendFile()
} else {
console.log('you cancelled')
}
set loading true/false in sendFile()
const sendFile = () => {
setLoading(true);
axios({
method: 'post',
url: '',
data: {
"uri": ""
}
}).then((res) =>{
setLoading(false)
addEntry(res.data)
}
).catch((err) => setLoading(false));
}
const renderItem = ({ item }) => (
<Text>{item.name}</Text>
);
add loading view in main return
return (
....
if(isLoading){
return(
<View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
<ActivityIndicator size="large"/>
</View>
);
}
....
)

React Native & Firebase Search

I am trying to use firebase and perform a search. The trouble I am having is when I type the search text the list data isn't being updated.
Below is the code I have come up with
componentDidMount() {
this.setState({loading: true});
var merchants = new Array;
merchantsRef.once('value').then((snapshot) => {
snapshot.forEach((child) => {
merchants.push({
id: child.val().id,
type: child.val().type,
attributes: {
coming_soon: child.val().attributes.coming_soon,
featured_image_url: child.val().attributes.featured_image_url,
has_locations: child.val().attributes.has_locations,
highlight: child.val().attributes.highlight,
logo_url: child.val().attributes.logo_url,
main_image_url: child.val().attributes.main_image_url,
name: child.val().attributes.name,
offer_active: child.val().attributes.offer_active,
offer_cta_title: child.val().attributes.offer_cta_title,
offer_ends_at: child.val().attributes.offer_ends_at,
offer_starts_at: child.val().attributes.offer_starts_at,
offer_teaser: child.val().attributes.offer_teaser,
offer_terms: child.val().attributes.offer_terms,
offer_text: child.val().attributes.offer_text,
offer_url: child.val().attributes.offer_url,
permalink: child.val().attributes.permalink,
shop_url: child.val().attributes.shop_url,
},
_key: child.key
})
})
this.setState({
data: merchants,
loading: false,
})
// console.log(this.state.data)
}).catch((error) => {
console.log('Error fetching retailer data:', error)
})
}
searchFilterFunction = text => {
merchantsRef.orderByChild('name').startAt(text.toString()).on('value', function(child) {
console.log(child.val());
});
};
_renderEmpty = () => {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<ActivityIndicator />
</View>
)
}
_renderItem = ({item}) => (
<MerchantRow data={item} />
)
_keyExtractor = (item, index) => item._key;
render() {
if (this.state.loading) {
console.log("Loading")
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<ActivityIndicator
animating
size="large"
/>
<Text>Fetching shops...</Text>
</View>
)
}
return (
<View style={styles.container}>
<SearchBar
placeholder='Search for retailers...'
containerStyle={{backgroundColor: Colors.blue, borderTopWidth: 0, borderBottomWidth: 0}}
inputStyle={{backgroundColor: Colors.snow}}
onChangeText={text => this.searchFilterFunction(text)}
autoCorrect={false}
/>
<ScrollView style={styles.container}>
<FlatList
data={this.state.data}
renderItem={this._renderItem.bind(this)}
keyExtractor={this._keyExtractor}
initialNumToRender={9}
ItemSeparatorComponent={this._renderSeparator}
removeClippedSubviews={false}
ListEmptyComponent={this._renderEmpty}
/>
</ScrollView>
</View>
)
}
#Paul'Whippet'McGuane, good to know. That's likely because the context of this isn't defined. In order to maintain the context of the keyword this, you can modify the searchFilterFunction like so:
searchFilterFunction = text => {
const that = this;
merchantsRef.orderByChild('name').startAt(text.toString()).on('value', function(child) {
console.log(child.val());
const val = child.val();
that.setState({ data: val });
});
};

Categories