I'm using react-native-material-menu's popup for showing menu options.
But the issue is, it's not working for multiple scenarios.
I mean when I click on first menu button, the same methods gets triggered and hence the same menu is opened every time.
What should be the better approach for to handle this particular scenario.
Here is the Snack
_menu = null;
setMenuRef = ref => {
this._menu = ref;
};
hideMenu = () => {
this._menu.hide();
};
showMenu = () => {
this._menu.show();
};
{this.state.clientsList.map((item) => {
return (
<View style={styles.caseItem} >
<Card style={styles.card}>
<CardItem>
<Body>
<View style={styles.rowTitle}>
<Text style={styles.title}>{item.FullName}</Text>
<Menu
ref={this.setMenuRef}
button={<Icon type="Feather" name="more-vertical" onPress={this.showMenu} style={{ fontSize: 20, color: '#555' }} />}
>
<MenuItem onPress={this.hideMenu}>View</MenuItem>
<MenuItem onPress={this.hideMenu}>Edit</MenuItem>
<MenuItem onPress={this.hideMenu}>Delete </MenuItem>
</Menu>
</View>
<View>
<Text style={styles.lbl}>Email: <Text style={styles.lblValue}>{item.EmailID}</Text></Text>
<Text style={styles.lbl}>Client Type: <Text style={styles.lblValue}>{item.ClientType}</Text></Text>
</View>
</Body>
</CardItem>
</Card>
</View>
);
})}
you are using same reference for all menu components
you have to use different ref for every Menu.
Best Approach:
first way to Create HOC for Menu and handle them in it.
Moderate
or Create Dynamic Ref in React for Menu's List
for running code only
3rd is to create ref for every menu
_menu1 = null;
_menu2 = null;
_menu3 = null;
You are calling the same ref every-time. I've not used the library you mentioned but if it has to rely on ref you can create a list of reference instead, syntax is in this post.
Related
my current issue with my react native app is that when a user wants to open a lesson (from the lessons array with each object being a lesson with a title,description,img url etc)to make it bigger through a modal, its state does not update. What i Mean by this is that the books title,description,and other attributes won't change if you press on a new lesson. What would be the solution to this?
export default function Learn() {
const [modalVisible, setModalVisible] = useState(false);
const [lessons,setLessons] = useState()
useEffect(() => {
async function data() {
try {
let todos = []
const querySnapshot = await getDocs(collection(db, "lessons"));
querySnapshot.forEach((doc) => {
todos.push(doc.data())
});
setLessons(todos)
console.log(lessons)
}
catch(E) {
alert(E)
}
}
data()
}, [])
return (
<View style={learnStyle.maincont}>
<View>
<Text style={{fontSize:28,marginTop:20}}>Courses</Text>
<ScrollView style={{paddingBottom:200}}>
{lessons && lessons.map((doc,key) =>
<>
<Modal
animationType="slide"
transparent={true}
visible={modalVisible}
onRequestClose={() => {
Alert.alert("Modal has been closed.");
setModalVisible(!modalVisible);
}}
>
<View style={styles.centeredView}>
<View style={styles.modalView}>
<Image source={{
uri:doc.imgURL
}} style={{width:"100%",height:300}}/>
<Text style={{fontWeight:"700",fontSize:25}}>{doc.title}</Text>
<Text style={{fontWeight:"700",fontSize:16}}>{doc.desc}</Text>
<Pressable
style={[styles.button, styles.buttonClose]}
onPress={() => setModalVisible(!modalVisible)}
>
<Text style={styles.textStyle}>Hide Modal</Text>
</Pressable>
</View>
</View>
</Modal>
<LessonCard setModalVisible={setModalVisible} title={doc.title} desc={doc.desc} img1={doc.imgURL} modalVisible={modalVisible}/>
</>
)}
<View style={{height:600,width:"100%"}}></View>
</ScrollView>
</View>
</View>
)
}
What it looks like:
**image 1 is before you press the modal and the 2nd one is after
**the main issue though is that if you press cancel and press on another lesson the modal that opens has the the same state(title,imgurl,anddesc) as the first lesson and does not change.
The problem is that you create a lot of modal windows through the map function, I suggest making one window and passing the key as a parameter and using it to search for a specific array of data that is shown to the user (photo, title, etc.)
The problem is that all 3 Modals are controlled by the one state variable. So when the code sets modalVisible to true, all 3 modals are being opened at once.
You can fix this in a few ways, but a simple way would be to move the Modal and its state into the LessonCard component. This way each modal will have its own state that's only opened by its card. So the loop in Learn will just be:
{lessons && lessons.map((doc,key) => (
<LessonCard lesson={doc} key={key} />
)}
Adding to address question in comments
LessonCard should not accept setModalVisible or modalVisible props. The
const [modalVisible, setModalVisible] = useState(false);
should be inside LessonCard, not Learn. That way each Card/Modal pair will have its own state.
Additionally, although React wants you to pass the key into LessonCard in the map function, LessonCard should not actually use the key prop for anything. See https://reactjs.org/docs/lists-and-keys.html#extracting-components-with-keys
So, the LessonCard declaration should just be something like
export default function LessonCard({lesson}) {
I'm a newbie to React/React Native, so please go easy on me. I've been stuck on this for a little while now so could use some help. Using functional React Native by the way.
How do I reference buttons that are in a card component from another screen? Using props in the card to display the toilet object's variables isn't a problem, but the buttons that are rendered through the card I can't work out how to reference them from the component with the map. Using navigation within the card doesn't work.
Screen that I want to reference the button in
{toilets.map((item, index) => {
return (
<ToiletCard
key={index}
title ={item.title}
address={item.address}
ratings={item.rating}
reviews={item.reviews}
onPress={() => {navigation.navigate("ReviewViewAndCreate", item)}}
/* I want to reference the review button using this onPress, but right now it isn't
referencing either button /*
/>
)
})}
Card component that contains the buttons
export default ToiletCard = (props) => {
return (
<View style = {styles.textContent}>
<View style = {{padding: 15}}>
<Text numberOfLine={1} style = {styles.listTitle}>{props.title}</Text>
<Text numberOfLine={1} style = {styles.listAddress}>{props.address}</Text>
<Text><StarRating ratings={props.ratings}/>{props.ratings}</Text>
<TouchableOpacity
style={styles.appButtonContainer}>
<Text style={styles.appButtonText}>Directions</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.appButtonContainerTwo}>
<Text style={styles.appButtonText}>Reviews</Text>
</TouchableOpacity>
</View>
<View style={styles.hairline}/>
</View>
)
}
Would appreciate any help.
Thanks!
Do these changes:
{toilets.map((item, index) => {
return (
<ToiletCard
key={index}
title ={item.title}
address={item.address}
ratings={item.rating}
reviews={item.reviews}
item={item}
navigation={navigation}
/* pass item as prop to this component /*
/>
)
})}
ToiletCard component:
export default ToiletCard = (props) => {
return (
<View style = {styles.textContent}>
<View style = {{padding: 15}}>
<Text numberOfLine={1} style = {styles.listTitle}>{props.title}</Text>
<Text numberOfLine={1} style = {styles.listAddress}>{props.address}</Text>
<Text><StarRating ratings={props.ratings}/>{props.ratings}</Text>
<TouchableOpacity
style={styles.appButtonContainer}>
<Text style={styles.appButtonText}>Directions</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={() => {props.navigation.navigate("ReviewViewAndCreate", props.item)}}
style={styles.appButtonContainerTwo}>
<Text style={styles.appButtonText}>Reviews</Text>
</TouchableOpacity>
</View>
<View style={styles.hairline}/>
</View>
)
}
I have a very frustrating situation. Trying to get keyboard to disappear and detect onPress event handler in child row.
Here is what my code looks like:
_renderRow = (prediction) => {
return (
<TouchableOpacity onPress={() => {
Keyboard.dismiss();
this.setState({ location: prediction.description });
}}>
<View style={styles.listItemContainer}>
<Text>{prediction.description}</Text>
</View>
</TouchableOpacity>
)
}
render() {
return (
<View style={styles.wrapper}>
{/* style={[this.state.predictions.length > 0 ? styles.searchContainerSuggest : styles.searchContainer]} */}
<View style={styles.searchContainerSuggest}>
<View style={{paddingLeft: 10, height: 45, display: 'flex', justifyContent: 'center'}}>
<TextInput
placeholder="Enter location"
value={this.state.location}
onChangeText={location => this.onChangeLocation(location)}
style={styles.textInput}
/>
</View>
{this.state.predictions.length && this.state.location !== '' ?
<FlatList
keyboardShouldPersistTaps={'handled'}
refreshing={!this.state.loaded}
initialNumToRender={10}
enableEmptySections={true}
data={this.state.predictions}
keyExtractor={(_, index) => index.toString()}
renderItem={ ({item: prediction}) => this._renderRow(prediction) } />
: null}
</View>
</View>
);
}
I probably need a helping hand or two with regards to how to debug this issue.
Looked up several examples on how to deal with hiding the keyboard and allowing a particular selection to be pressed at the same time.
I thought that keyboardShouldPersistTaps would allow for the child selection to be selected. Upon selection, the onPress event handler will trigger and that will be where I call Keyboard.dismiss() to hide the keyboard. Does not seem to work.
In my case, besides adding keyboardShouldPersistTabs='handled' to the FlatList in question, it was also needed to add keyboardShouldPersistTabs='handled' and nestedScrollEnabled={true} to a parent ScrollView like 2 levels above, wrapping the FlatList I intended to get this behavior with. Check out this issue in react-native repo for more info.
For anyone who is running into the same problem as me. Check whether your FlatList or ScrollView is nested in another FlatList or ScrollView.
If yes, then add
keyboardShouldPersistTaps='handled'
to the element as a props as well.
add keyboardDismissMode="none" to FlatList
I am using a GridView to show multiple products and on click i want to go to details screen , however I am unable to find a way to identify which product grid view cell was selected / tapped by user , I think sending some parameter to handle method should do the trick.
return (
<GridView
itemDimension={130}
items={this.state.dataSource}
style={styles.gridView}
renderItem={item => (
<View style={styles.itemContainer}>
<TouchableOpacity onPress={ this.onPressStone } style={styles.itemContainer}>
<Image source = {{uri:item.url}} style={styles.imageView} />
<Text style={styles.itemName}>{item.name}</Text>
</TouchableOpacity>
</View>
)}
/>
);
You can send parameters to your function like you said.
Example
onPress={ () => this.onPressStone(item.id) }
You can pass to the method onPressStone the item and depending the item you could do that print what you want. And if you want to know who was pressed you just have to make a log with the name of the item or take the index for example
return (
<GridView
itemDimension={130}
items={this.state.dataSource}
style={styles.gridView}
renderItem={(item, index) => {
//to know who was pressed:
console.log('pressedItemName-->', item.name);
//to know index pressed:
console.log('pressedItemName-->', item);
return (
<View style={styles.itemContainer}>
<TouchableOpacity onPress={ ()=> {this.onPressStone(item)} } style={styles.itemContainer}>
<Image source = {{uri:item.url}} style={styles.imageView} />
<Text style={styles.itemName}>{item.name}</Text>
</TouchableOpacity>
</View>
)}
/>
);
I am using react native with NativeBase components, and I am having a problem calling my _doStuff function. I tried to call
onPress={this._doStuff.bind(this)}
but keep receiving
Cannot read properly 'doStuff' of undefined
_doStuff(){
console.log('Hi');
}
_getList() {
return this.state.listData.map(function(data, i){
return(
<View key={i}>
<ListItem style={styles.listItemContain}>
<Button transparent onPress={this._doStuff.bind(this)}>
<View>
<Thumbnail source={{uri: data.icon}} style={styles.thumbnailStyle}/>
<Text style={styles.title}>{data.name}</Text>
<Text note style={styles.text}>{data.vicinity}</Text>
</View>
</Button>
</ListItem>
</View>
);
});
this context is lost inside the .map loop. if you are using ES6, try to use fat arrow functions as shown below.
_getList() {
return this.state.listData.map((data, i) => {
return(
<View key={i}>
<ListItem style={styles.listItemContain}>
<Button transparent onPress={this._doStuff.bind(this)}>
<View>
<Thumbnail source={{uri: data.icon}} style={styles.thumbnailStyle}/>
<Text style={styles.title}>{data.name}</Text>
<Text note style={styles.text}>{data.vicinity}</Text>
</View>
</Button>
</ListItem>
</View>
);
});
if you cant use ES6 on your project due to various reasons use alternative approach(old school) as shown below...
_getList() {
return this.state.listData.map(function(data, i){
var that = this;
return(
<View key={i}>
<ListItem style={styles.listItemContain}>
<Button transparent onPress={that._doStuff.bind(that)}>
<View>
<Thumbnail source={{uri: data.icon}} style={styles.thumbnailStyle}/>
<Text style={styles.title}>{data.name}</Text>
<Text note style={styles.text}>{data.vicinity}</Text>
</View>
</Button>
</ListItem>
</View>
);
});
I would make a wager that the context of your _getList function does not have the context of the React Component that you are thinking. If you are using ES6 classes and would like to get autobinding of the components context I would suggest using autobind-decorator. If you decide to go that route here is a nice tutorial on setting it up.
Otherwise make sure that when you call _getList make sure to call it with either .call or .bind.
For example:
// if you are calling the function directly
this._getList().call(this)
or
// if you are passing the function off for another function to execute it
this._getList.bind(this)