useEffect not working in custom drawer component without refresh - react-native

So I am using react-navigation 5 and I have a custom drawer component for my app. I want to display the name of the logged-in user in the drawer for which I am using a state variable and I am updating the state from firestore. I am calling a function in useEffect which accesses firestore and gets the name of the user. But I think the useEffect is not working without refresh because unless I save the project and refresh the application the state is not getting updated in the application and I cannot see the name of the user without refreshing but it is visible after a refresh. Any ideas why this is happening? Any help would be appreciated. Thank you.
Custom drawer
export default function CustomDrawer(props) {
const paperTheme = useTheme();
const [name,setName]=useState('');
useEffect(() => {
doStuff();
}, []);
const doStuff = async () => {
var phone=global.phone;
await firestore().collection("Users").where('Phone Number', '==', phone).get().then(querySnapshot=>{
querySnapshot.forEach(documentSnapshot => {
console.log("in drawer");
console.log(documentSnapshot.data());
setName(documentSnapshot.data().Name);
})
})
};
return(
<View style={{flex:1}}>
<DrawerContentScrollView {...props}>
<View style={styles.drawerContent}>
<View style={styles.userInfoSection}>
<View style={{flexDirection:'row',marginTop: 15}}>
<Avatar.Image
source={{
uri: ''
}}
size={50}
/>
<View style={{marginLeft:15, flexDirection:'column'}}>
<Title style={styles.title}>{name}</Title>
</View>
</View>
</View>
</View>
</DrawerContentScrollView>
</View>
);
}

Looks like you have doStuff function defined outside the useEffects.
Either you need to put it inside useEffects or add it in dependency list
useEffect(() => {
doStuff();
}, [doStuff]);

Related

Strange behavior using useState to load data into a object state

i have broken my head trying to understand a problem with my code. I'm new with React Native so there may be a standard behavior that i am unaware of. This is my problem:
In my component i have a useEffect() to load my data like "componentDidMount":
useEffect( () => {
async function loadDadosLista(){
let listaRecebida = await getListaByID(route.params.idLista);
setLista(listaRecebida);
};
loadDadosLista();
}, []);
My function works correctly, the function getListaById accesses my realm.db and return my object lista. After that I can access the data and associate it with components of type TextInput. My real problem is that any change the i do in any component using properties of lista, overwrites all data leaving only the one that has been modified. I'm using spread operator but apparently it doesn't work. Below is my complete code for better understanding.
function ListConfig(){
const [lista, setLista] = useState({});
useEffect( () => {
async function loadDadosLista(){
let listaRecebida = await getListaByID(route.params.idLista);
setLista(listaRecebida);
};
loadDadosLista();
}, []);
return(
<View style={styles.container}>
<View style={[styles.containerLinha, styles.linha2]}>
<View style={styles.inputLocal}>
<TextInput
name='estabelecimento'
placeholder='Venda do seu Francisco'
placeholderTextColor={theme.colors.cinzaPrimario}
style={styles.textInputLocal(theme)}
value={lista.estabelecimento}
maxLength={25}
onChangeText={ (value) => {
setLista({
...lista,
estabelecimento: value
})
}}
textAlignVertical='bottom'
/>
<IconLocation width={20} height={24} />
</View>
</View>
<View style={styles.containerNotif}>
<Text style={styles.textoNotif(theme)}>
Me notifique 20 minutos antes
</Text>
<ToggleSwitch
isOn={lista.notificacaoAtiva}
onColor={theme.colors.cinzaSecundario}
offColor={theme.colors.cinzaSecundario}
thumbOnStyle={{
backgroundColor: theme.colors.destaque
}}
size="medium"
onToggle={(isOn) => {
setLista({
...lista,
notificacaoAtiva: isOn
});
}}
/>
</View>
</View>
);
}
export default ListConfig;
My object lista have this properties:
{
estabelecimento: 'nameOfEstabelecimento',
notificacaoAtiva: true
}

Calling modal on a list of products opens the modal for all of them instead of just the one being clciked

I am making a react native app that loads data from google firebase and then display it on a page, when a user clicks on any of the products aa modal will open to show more datails.
I am using useEffect to load the data on page load then display then results:
const fetchData = async () => {
const categories = db.collection("productsDB");
const collections = await categories
.limit(6)
.onSnapshot((querySnapshot) => {
const items = [];
querySnapshot.forEach((documentSnapshot) => {
items.push({
...documentSnapshot.data(),
key: documentSnapshot.id,
});
});
setItems(items);
setLoading(false);
});
return () => collections();
};
useEffect(() => {
fetchData();
}, []);
and the show them like this:
{loading ? (
<ActivityIndicator />
) : (
items.map((item) => (
<TouchableOpacity
style={styles.queryResult}
key={item.key}
onPress={() => {
setModalVisible(!modalVisible);
}}
>
<View style={styles.queryResultContent}>
<Image
style={{ width: 100, height: 100 }}
source={{ uri: String(item.images) }}
/>
<View>
<Text style={styles.queryInfoHeader}>{item.name}</Text>
</View>
</View>
<View>
<ProductModal
isModalVisible={modalVisible}
setModalVisible={setModalVisible}
navigation={navigation}
{...item}
/>
</View>
</TouchableOpacity>
))
)}
when I open the modal, it opens the modal for all of the products and doesnt really matter if I click on the first product or what, it opens all of the modals, and I am not sure how to get rid of this!
is there any better way to write this function?
You're using the same modalVisible flag for all of your modals; therefore, they either are all visible or all hidden.
Why not have a single modal rather than rendering a bunch of them in the loop, and pass the item as a prop to it?

React Native ref is undefined until page reload

I am using refs to animate a View, but the refs are throwing an undefined error. However, if I comment-out my attempt to access the ref, load the page, un-comment the code, & reload the page, the animation works fine.
My code:
export const AccountScreen = ({ navigation }) => {
return (
<SafeAreaView style={{ backgroundColor:'#FFFFFF', flex: 1 }}>
<Animatable.View style={styles.container} ref={ref => {this.containerRef = ref}}>
</Animatable.View>
</SafeAreaView>
)
};
launchOpenAnimation()
function launchOpenAnimation () {
this.containerRef.animate(appearContainerAnimation); //If I comment this out, reload, and un-comment, it works
}
What can I do to fix this? It appears that the ref isn't defined at the time that my launchOpenAnimation() is executed, but that it is defined after, thus resulting in the code working if I comment it out, reload, un-comment, and reload again.
First thing first, your question is messed up between calls component and functional component, cuz there is no this in functional component. I'll convert them to function component using useRef and useEffect
In the first run this.containerRef have not be assign to ref of Animatable yet.
Try this
export const AccountScreen = ({ navigation }) => {
const animateRef = useRef()
useEffect(() =>{
launchOpenAnimation()
function launchOpenAnimation () {
// add ? to make sure animate is called only when this.containerRef exists
containerRef?.current.animate(appearContainerAnimation);
}
},[])
return (
<SafeAreaView style={{ backgroundColor:'#FFFFFF', flex: 1 }}>
<Animatable.View style={styles.container} ref={ref => {
ref.animate(appearContainerAnimation); // add this if you need to run when initial render run
animateRef.current = ref
}}>
</Animatable.View>
</SafeAreaView>
)
};

React Navigation is not a function?

I'm using React Navigation. I'm directing you to the conversation after the record insert page. I provide automatic renewal when I redirect. I'm sending a parameter. But that doesn't work. I don't know where I'm making a mistake.
I get this error.
Uncaught typeerror. TypeError: s.handleRefresh is not a function. (In 's.handleRefresh()', 's.handleRefresh' is undefined
LeadDetail.js Page
static navigationOptions = ({ navigation }) => {
const { params = {} } = navigation.state;
return {
title: 'Müşteri Adayı Detay',
headerRight: (
<TouchableHighlight activeOpacity={0.4} underlayColor='transparent' onPress={() => navigation.navigate('CreateMeeting', { parentId: params.id, parentType: 'Lead' })} style={{marginRight: 12 }}>
<Icon
name="calendar-check-o"
size={25}
color="white"
/>
</TouchableHighlight>
)
};
};
CreateMeeting.js Page
dataSuccess(response) {
this.setState({ buttonLoading: false });
Alert.alert(
'Tebrikler!',
'Meeting başarılı bir şekilde kayıt edildi',
[
{ text: 'Tamam', onPress: () => this.props.navigation.navigate('Meeting', { refreshMeeting: true }) }
]
);
}
Meeting.js Page
static navigationOptions = ({ navigation }) => {
const { params } = navigation.state;
let refresh = navigation.getParam('refreshMeeting', false);
if(refresh) {
params.handleRefresh();
}
return {
title: 'Görüşmeler',
headerRight: (
<View style={{ flexDirection: 'row' }}>
<TouchableHighlight activeOpacity={0.4} underlayColor='transparent' onPress={navigation.getParam('setOverlay')}>
<Icon
name="filter"
size={25}
color="white"
/>
</TouchableHighlight>
<TouchableHighlight activeOpacity={0.4} underlayColor='transparent' onPress={navigation.getParam('setModalVisible')} style={{ marginRight: 12, marginLeft: 20 }}>
<Icon
name="plus"
size={25}
color="white"
/>
</TouchableHighlight>
</View>
)
};
};
Are you setting the param handleRefresh? Instead of params.handleRefresh(); shouldn't it be params.refresh();?
Also, s.handleRefresh where is this in your code? I think there is a typo or something like that and I'm considering that s.handleRefresh is actually params.handleRefresh
Please edit your question if none of this is correct.
I don't know where you are setting the param handleRefresh, but if you are setting it (please check that and add the code where you do that), maybe here is the reason why this happens.
If you look at the docs they have a not
React Navigation doesn't guarantee that your screen component will be mounted before the header. Because the increaseCount param is set in componentDidMount, we may not have it available to us in navigationOptions. This usually will not be a problem because onPress for Button and Touchable components will do nothing if the callback is null. If you have your own custom component here, you should make sure it behaves as expected with null for its press handler prop.
This means that the header can render before the component calls setParams and that doens't load you params in the first time. What you need to do some checking to avoid that time when the header renders first.
// added some checking
if(refresh && params && params.handleRefresh) {
params.handleRefresh();
}

Trying to connect to api with link stored in two components

I'm trying to fetch data from API, using the link combined from link fetched from another component and API token, but the API is not returning anything.
I suspect the connection to API might be correct and the issue lies with wrongly fetching specific objects from the API structure. api structure
componentDidMount(){
const { navigation } = this.props;
const linkPlant = navigation.getParam('linkPlant');
fetch(linkPlant + '?token=/////FY03yEVzS77Ca1Q9TIbMdMlJhXtpOjhcqcD-MJHA')
.then(response => response.json())
.then((responseJson)=> {
this.setState({
loading: false,
dataSource: responseJson
})
})
.catch(error=>console.log(error))
}
renderItem=(data)=>
<TouchableOpacity style={styles.list}>
<Text style={styles.lightText}>{data.item.varieties[0].common_name}</Text>
</TouchableOpacity>
render(){
return(
<View>
<FlatList
data= {this.state.dataSource}
renderItem= {item=> this.renderItem(item)}
keyExtractor= {item=>item.id.toString()} />
</View>
</ScrollView>
);}
{!loading?
<View>
<FlatList
data= {this.state.dataSource}
renderItem= {item=> this.renderItem(item)}
keyExtractor= {item=>item.id.toString()} />
</View>
: <View><Text>Loading</Text></View>}
You've defined an loading promise but never used that. Maybe using it for render when page is loaded could be fix that error.