How to access one functional component's state from another functional component in react-native? - react-native

I want to access ModalView's state from MarkerView component. Actually i want to see ModalView when i click Get info. button which is in MarkerView. I want to set { setVisiblity(true)} from MarkerView component. How can i do it?
ModalView.js
const ModalView = () => {
const [visiblity, setVisiblity] = useState(false);
return (
<Modal transparent={false} visible={visiblity} >
<TouchableOpacity onPress={() => { setVisiblity(false)}}>
<Text> Submit </Text>
</TouchableOpacity>
</Modal>
)
}
MarkerView.js
const MarkerView = () => {
return (
// i want to set visiblity true from here
<View>
<TouchableOpacity onPress={() => { setVisiblity(true) }}>
<Text>Get info.</Text>
</TouchableOpacity>
</View>
)
}
App.js
import ModalVIew from './components/ModalView';
import Marker from './components/MarkerView';
const App = () => {
return (
<View>
<Marker/>
<ModalVIew/>
</View>
)
}
export default App;

you can use state management like contextAPI or redux, or you can put your state on your higher order component but this will result in some prop drilling.
App.js
const App = () => {
const [visiblity, setVisiblity] = useState(false);
return (
<View>
<Marker visiblity={visiblity} onChangeVisiblity={(val) => setVisiblity(val)}/>
<ModalVIew visiblity={visiblity} onChangeVisiblity={(val) => setVisiblity(val)}/>
</View>
)
}
MarkerView.js
const MarkerView = ({visiblity, onChangeVisiblity: changeVisiblity}) => {
return (
<View>
<TouchableOpacity onPress={() => changeVisiblity(true)}>
<Text>Get info.</Text>
</TouchableOpacity>
</View>
)
}
ModalView.js
const ModalView = ({visiblity, onChangeVisiblity:changeVisiblity}) => {
const [visiblity, setVisiblity] = useState(false);
return (
<Modal transparent={false} visible={visiblity} >
<TouchableOpacity onPress={() => changeVisiblity(false)}>
<Text> Submit </Text>
</TouchableOpacity>
</Modal>
)
}

Related

How do I get setState to update a value immediately?

I have to press the buttons twice to update the filter value for how I want to display the movies in my app. This is my code:
const HomeScreen = () => {
const navigation = useNavigation();
const [movies, setMovies] = useState({});
useEffect(() => {
getMovies();
},[])
useEffect(() => {
getMoviesFiltered(filter);
},[filter])
const [filter, setFilter] = useState('name');
const getMovies = async (filter) =>{
const querySnapshot = await getDocs(query(collection(db, "movies"), orderBy(filter)));
setMovies(querySnapshot.docs);
}
return (
<View>
<View>
<TouchableOpacity onPress = {() => {setFilter('name'); getMovies(filter)}}>
<Text>Title</Text>
</TouchableOpacity>
<TouchableOpacity onPress = {() => {setFilter('runtime'); getMovies(filter)}}>
<Text>Runtime</Text>
</TouchableOpacity>
<TouchableOpacity onPress = {() => {setFilter('year'); getMovies(filter)}}>
<Text>Year</Text>
</TouchableOpacity>
</View>
</View>
<FlatList
data = {movies}
renderItem = {({item}) => (
<TouchableOpacity onPress={() => navigation.navigate('Modal', item.data())}>
<Image source={{uri: item.data().pic}}/>
</TouchableOpacity>
)
}
/>
</View>
)
}
export default HomeScreen
I know that setState is asynchronous and that that is the reason it happens, but I'm kind of stuck on not knowing how to change it properly, so I'd appreaciate the help. Thank you.
useEffect will run when the values within dependency array change.
So, you don't need to add function getMovies after you changed your filter. Just simply move that to useEffect.
const HomeScreen = () => {
const navigation = useNavigation();
const [movies, setMovies] = useState({});
const [filter, setFilter] = useState('name');
useEffect(() => {
//when page is initialize, run this
getMovies(filter);
},[])
useEffect(() => {
//when filter is changed, use latest value to run this
getMovies(filter);
},[filter]);
const getMovies = async (filter) =>{
const querySnapshot = await getDocs(query(collection(db, "movies"), orderBy(filter)));
setMovies(querySnapshot.docs);
}
return (
<View>
<View>
<TouchableOpacity onPress = {() => {setFilter('name');}}>
<Text>Title</Text>
</TouchableOpacity>
<TouchableOpacity onPress = {() => {setFilter('runtime');}}>
<Text>Runtime</Text>
</TouchableOpacity>
<TouchableOpacity onPress = {() => {setFilter('year');}}>
<Text>Year</Text>
</TouchableOpacity>
</View>
<FlatList
data = {movies}
renderItem = {({item}) => (
<TouchableOpacity onPress={() => navigation.navigate('Modal', item.data())}>
<Image source={{uri: item.data().pic}}/>
</TouchableOpacity>
)
}
/>
</View>
)
}
export default HomeScreen

Why my virtualized list does not re-render when the data passed as prop actualizes?

I am having a problem that I can't solve by my own. I am making an app for making lists in React Native, and in my main screen, that shows the session initiated by the user, I have to render all the lists that he had saved previously. Here is the code of my session component.
export default function Session({navigation,route}){
const {user} = useContext(myContext)
const [modalVisible, setModalVisible] = useState(false)
const [lists, setLists] = useState(route.params.lists)
let keyListCounter = 0
const handleButton = async () => {
await AsyncStorage.removeItem("token")
navigation.navigate("Login")
}
const updateList = (title,newElement) => {
axios.put(`http://${REACT_APP_BACK_URI}/api/lists/add-list-element`, {nickname: user,title,element: newElement})
.then(res => {
if (res.status == 200) {
setLists(res.data.userLists)
}
})
.catch(err => console.log(err))
}
useEffect(() => {
navigation.setOptions({
title: user,
headerTitleAlign: "left",
headerRight: () => (
<TouchableWithoutFeedback onPress={() => handleButton()}>
<Text style={styles.logoutText}>Logout</Text>
</TouchableWithoutFeedback>
)
})
},[navigation,user])
return (
<View style={styles.mainContainerView}>
<ScrollView style={styles.mainContainerScrollView}>
<View style={styles.textListContainer}>
<Text style={styles.listsText}>LISTAS ACTIVAS: </Text>
<Text style={styles.numberListsText}>{lists.length}</Text>
</View>
{lists.map(elem => <List key={keyListCounter++} list={elem} updateList={updateList}/>)}
</ScrollView>
<Pressable style={styles.newListPressable} onPressIn={() => setModalVisible(true)}>
<Text style={styles.newListText}>+</Text>
</Pressable>
<View style={styles.centeredView}>
<Modal
visible={modalVisible}
animationType="slide"
transparent={true}
>
<View style={styles.centeredView}>
<View style={styles.modalView}>
<Text>MODAL</Text>
</View>
</View>
</Modal>
</View>
</View>
)
}
My question is why after I actualize the state of "lists", whose elements are passed as props to the List component, the virtualized list that I have in the List component does not re-renderizes automaticaly.
Here I show also the code of the List component.
export default function List({list,updateList}){
const {elements, title} = list
let elementId = 0
const virtualizedList = useRef()
const [showVirtualizedList, setShowVirtualizedList] = useState("none")
const [showDownArrow, setShowDownArrow] = useState(true)
const [showUpArrow, setShowUpArrow] = useState(false)
let [newElementArray, setNewElementArray] = useState([])
let [listElements, setListElements] = useState(elements)
const getItem = (item) => ({
id: elementId++,
title: item
});
//List Pressable Events
const handlePressIn = () => {
if (showVirtualizedList == "none") setShowVirtualizedList("flex")
else setShowVirtualizedList("none")
setShowDownArrow(!showDownArrow)
setShowUpArrow(!showUpArrow)
}
//New element Pressable Events
const handleNewElement = () => {
setNewElementArray([...newElementArray,uuid.v4()])
}
//NewListItem TouchableWithoutFeedback Events
const deleteElementInput = newItemID => {
const elementsArray = newElementArray.filter(elem => elem != newItemID)
setNewElementArray(elementsArray)
}
const addListElement = (newElement,newItemID) => {
updateList(title,newElement)
deleteElementInput(newItemID)
}
useEffect(() => {
virtualizedList.current.setNativeProps({display: showVirtualizedList})
LogBox.ignoreLogs(['VirtualizedLists should never be nested']);
},[showVirtualizedList, virtualizedList])
return (
<ScrollView style={styles.mainContainer}>
<Pressable
style={styles.listElement}
onPressIn={() => handlePressIn()}
>
<View style={styles.titleContainer}>
<Text style={styles.listElementText}>{title} </Text>
<Text style={styles.listElementQuantity}>({listElements.length})</Text>
</View>
<View>
<DownArrow show={showDownArrow}/>
<UpArrow show={showUpArrow}/>
</View>
</Pressable>
<View>
<VirtualizedList
data={listElements}
initialNumToRender={10}
getItemCount={() => listElements.length}
renderItem={({item}) => <ListItem item={item}/>}
getItem={() => getItem(listElements[elementId])}
ref={virtualizedList}
/>
</View>
{newElementArray.length > 0 ? newElementArray.map(elem => {
return (
<NewListItem
key={elem}
id={elem}
newElementArray={newElementArray}
deleteElementInput={deleteElementInput}
addListElement={addListElement}
/>
)
}) : ""
}
<Pressable style={styles.newElementPressable} onPressIn={() => handleNewElement()}>
<Text style={styles.newElementText}>+</Text>
</Pressable>
</ScrollView>
)
}
UPDATE: I solve the problem using a FlatList instead of a VirtualizedList. For some reason the FlatList re-renders when the Item is updated and the VirtualizedList no. I don't know why.....

react native navigation refresh component problem

I'm in category page and when i press any of them, it navigates me to the that categories product list. so far so good. when i go back and press another category i get previous categories product list.
there is my some of code
this is how i navigate to product list
render() {
const {navigation} = this.props.navigation;
const categories = category.map((item, index) => {
return (
<TouchableOpacity
key={index}
styles={styles.category}
onPress={() =>
navigation.navigate('ProductList', {
categoryName: item.Menu,
})
}>
<Text> {item.Menu} </Text>
</TouchableOpacity>
);
});
return (
<View style={styles.container}>
{this.state.isData ? (
<View style={styles.container}>
<Text style={styles.title}>Category</Text>
{categories}
</View>
) : (
<ActivityIndicator size="large" color="#0B970B" />
)}
</View>
);
}
}
and this is where i go, i can get navigation props ext. but i cant find where the problem is
import React,{useState,useEffect} from 'react';
import {
View,
ScrollView,
Text,
Image,
StyleSheet,
Dimensions,
ActivityIndicator,
} from 'react-native';
const List = ({navigation}) => {
const [isData, setIsData] = useState(false);
useEffect(() => {
getData();
return null;
}, []);
const getData = async () => {
if (navigation?.route?.params?.categoryName) {
categoryName = navigation.route.params.categoryName;
fetch(global.apiPost + global.token, requestOptions)
.then((response) => response.json())
.then((result) => {
result.forEach((element) => {
if (element.Menu === categoryName) {
products.push(element);
}
});
setIsData(true);
console.log(products, 'products');
console.log(productsByAccessory, 'productsByAccessory');
console.log(productsByTravel, 'productsByTravel');
console.log(productsByBag, 'productsByBag');
})
.catch((error) => console.log('error', error));
}
};
return (
<View style={{flex: 1}}>
<Text style={styles.title}>{categoryName}</Text>
{isData ? (
<View style={{flex: 1}}>
<View style={styles.itemFounds}>
<Text>{data.length + ' item founds'}</Text>
</View>
<View style={{flexDirection: 'row', flexWrap: 'wrap'}}>
<Text>
{productsByAccessory.length} / {productsByTravel.length} /{' '}
{productsByBag.length} / {products.length} asd
</Text>
</View>
</View>
) : (
<ActivityIndicator size="large" color="#0B970B" />
)}
</View>
);
}
export default List
Change your render method
const { navigation } = this.props; // Here was the error
const categories = category.map((item, index) => {
return (
<TouchableOpacity
key={index}
styles={styles.category}
onPress={() =>
navigation.navigate("ProductList", {
categoryName: item.Menu,
})
}
>
<Text> {item.Menu} </Text>
</TouchableOpacity>
);
});
render() {
return (
<View style={styles.container}>
{this.state.isData ? (
<View style={styles.container}>
<Text style={styles.title}>Category</Text>
{categories}
</View>
) : (
<ActivityIndicator size="large" color="#0B970B" />
)}
</View>
);
}
}
render method should have the return statement only. Don't perform any operations inside the render method. Now it should work.

get product id set in const use onPress in react native loop

I want to add key={item.id} value in pid use onPress, and her data come to data base use loop in react native
const addToWishlist = () => {
const [pid, setPid] = useState('');
}
return (
<>
{ProductData.map((item, index) => {
return (
<View key={index} style={styles.prod}>
<TouchableOpacity onPress={addToWishlist} key={item.id}>
<Feather name="heart" color={heartColor} style={{fontSize:16}}/>
</TouchableOpacity>
</View>
)
})}
</>
)
Your function should accept an arg id and you must send it when the onPress is triggered.
onPress={() => addToWishlist(item.id)}
const addToWishlist = (id) => {
console.log(id)
// const [pid, setPid] = useState('');
// You should not declare a hook in a function
}
return (
<>
{ProductData.map((item, index) => (
<View key={index} style={styles.prod}>
<TouchableOpacity onPress={() => addToWishlist(item.id)} key={item.id}>
<Feather name="heart" color={heartColor} style={{fontSize:16}}/>
</TouchableOpacity>
</View>
)})
</>
)

How to use TouchableOpacity to hide view by in React Native?

I'm really new to React Native and I'm wondering how can I hide/show View
Here's my test code:
class Counter extends React.Component{
state = { count:0 };
setCount = () => this.setState(
prevState => ({ ...prevState, count: this.state.count + 1 })
)
render(){
const { count } = this.state;
const [valueLocation, onChangeText] = React.useState('Pleas input Address');
const [value, onChangeEvent] = React.useState('Your questions');
return (
<ScrollView style={styles.header}>
<View style={styles.box1}>
<View style={styles.box2}>
<View style={styles.user}>
<Image
style={styles.userImg}
source={{
uri: event.user[0].image,
}}
/>
<View style={styles.userText}>
<Text style={styles.username}>{event.user[0].name}</Text>
<Text style={styles.date}>{event.user[0].date}</Text>
</View>
</View>
<View style={styles.boxHidebtn}>
<View style={styles.EventClass}>
<Text style={styles.btn_text_white}>類型</Text>
</View>
<TouchableOpacity
style={styles.EventOpen}
onPress={this.setCount}
>
<Text>></Text>
</TouchableOpacity>
</View>
</View>
<View style={count % 2 ? styles.box3 : styles.box3Open}>
<Text style={styles.address}>台北市市民大道六段37號</Text>
<Text style={styles.eventShow}>路上坑洞造成積水</Text>
</View>
</View>
</ScrollView>
);
}
}
const App = () => {
<Counter/>
};
const styles = StyleSheet.create({
....
});
export default App;
I run my code and it tell me
"App(...): Nothing was returned from render. This usually means a return statement is missing. Or, to render nothing, return null.
thanks!!!!!!!!
It looks like your arrow function needs to return the Counter:
const App = () => {
return <Counter/>;
};
Or, simply:
const App = () => <Counter/>;
I can see three errors in your code:
const App = () => {
return <Counter/>; // here you should return Counter
};
prevState -> this.state I guess
setCount = () => this.setState({ ...this.state, count: this.state.count + 1 }); // here
you have also a syntax error <Text>></Text> remove this extra closing > inside touchableopacity
Regarding your question in the title? I can't see where you want to hide the view?