What I am trying to achieve is to open a modal dialog when the user press on one of the items of a FlatList ponent. The code of this list looks like this:
class MyItem extends React.Component {
_onPress = () => {
this.props.onPressItem(this.props.item);
};
render() {
return(
<TouchableOpacity
{...this.props}
onPress={this._onPress}
>
<Text style={styles.itemText}> {this.props.item.name}</Text>
</TouchableOpacity>
)
}
}
export default class MyList extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
data: {}, // some data correctly loaded
isModalVisible: false
};
};
_onPressItem = (item) => {
this._showModal;
};
_showModal = () => this.setState({ isModalVisible: true })
_keyExtractor = (item, index) => item.id;
_renderItem = ({item}) => (
<MyItem
style={styles.row}
item={item}
onPressItem={this._onPressItem}
/>
);
render() {
return(
<KeyboardAvoidingView behavior="padding" style={styles.container}>
<View style={styles.titleContainer}>
<Text style={styles.title}>Tittle</Text>
</View>
<ScrollView style={styles.container}>
<FlatList
data={this.state.data}
ItemSeparatorComponent = {this._flatListItemSeparator}
renderItem={this._renderItem}
keyExtractor={this._keyExtractor}
/>
</ScrollView>
<MyModal modalVisible={this.state.isModalVisible}/>
</KeyboardAvoidingView>
);
}
}
Styles, FlatList data and some functions have been removed because they are not relevant for this issue.
As you can see, MyModal
ponent is declared after ScrollView
ponent. The code is based on the use of react-native Modal ponent:
export default class MyModal extends Component {
constructor(props) {
super(props);
this.state = {
isModalVisible: props.modalVisible
};
};
_setModalVisible(visible) {
this.setState({modalVisible: visible});
}
render() {
return (
<View>
<Modal
animationType="slide"
transparent={false}
visible={this.state.modalVisible}
onRequestClose={() => {alert("Modal has been closed.")}}
>
<View style={styles.container}>
<View style={styles.innerContainer}>
<Text>Item Detail</Text>
<TouchableHighlight
style={styles.buttonContainer}
onPress={() => { this._setModalVisible(false) }}>
<Text style={styles.buttonText}>Close</Text>
</TouchableHighlight>
</View>
</View>
</Modal>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
padding: 20,
backgroundColor: 'transparent',
},
innerContainer: {
borderRadius: 10,
alignItems: 'center',
backgroundColor: '#34495e',
},
buttonContainer: {
paddingVertical: 15,
marginTop: 20,
backgroundColor: '#2c3e50',
borderRadius: 15
},
buttonText: {
textAlign: 'center',
color: '#ecf0f1',
fontWeight: '700'
},
});
Current behaviour is MyModal
ponent being displayed when I access to MyList
ponent for the first time, I can close it and then the FlatList
is there but when a list item is pressed, MyModal
ponent is not displayed.
How can I manage to hide Modal and open it only when a list item is pressed?
Another doubt related to this is:
How to pass the pressed item object to MyModal
ponent?
Thanks in advance!
What I am trying to achieve is to open a modal dialog when the user press on one of the items of a FlatList ponent. The code of this list looks like this:
class MyItem extends React.Component {
_onPress = () => {
this.props.onPressItem(this.props.item);
};
render() {
return(
<TouchableOpacity
{...this.props}
onPress={this._onPress}
>
<Text style={styles.itemText}> {this.props.item.name}</Text>
</TouchableOpacity>
)
}
}
export default class MyList extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
data: {}, // some data correctly loaded
isModalVisible: false
};
};
_onPressItem = (item) => {
this._showModal;
};
_showModal = () => this.setState({ isModalVisible: true })
_keyExtractor = (item, index) => item.id;
_renderItem = ({item}) => (
<MyItem
style={styles.row}
item={item}
onPressItem={this._onPressItem}
/>
);
render() {
return(
<KeyboardAvoidingView behavior="padding" style={styles.container}>
<View style={styles.titleContainer}>
<Text style={styles.title}>Tittle</Text>
</View>
<ScrollView style={styles.container}>
<FlatList
data={this.state.data}
ItemSeparatorComponent = {this._flatListItemSeparator}
renderItem={this._renderItem}
keyExtractor={this._keyExtractor}
/>
</ScrollView>
<MyModal modalVisible={this.state.isModalVisible}/>
</KeyboardAvoidingView>
);
}
}
Styles, FlatList data and some functions have been removed because they are not relevant for this issue.
As you can see, MyModal
ponent is declared after ScrollView
ponent. The code is based on the use of react-native Modal ponent:
export default class MyModal extends Component {
constructor(props) {
super(props);
this.state = {
isModalVisible: props.modalVisible
};
};
_setModalVisible(visible) {
this.setState({modalVisible: visible});
}
render() {
return (
<View>
<Modal
animationType="slide"
transparent={false}
visible={this.state.modalVisible}
onRequestClose={() => {alert("Modal has been closed.")}}
>
<View style={styles.container}>
<View style={styles.innerContainer}>
<Text>Item Detail</Text>
<TouchableHighlight
style={styles.buttonContainer}
onPress={() => { this._setModalVisible(false) }}>
<Text style={styles.buttonText}>Close</Text>
</TouchableHighlight>
</View>
</View>
</Modal>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
padding: 20,
backgroundColor: 'transparent',
},
innerContainer: {
borderRadius: 10,
alignItems: 'center',
backgroundColor: '#34495e',
},
buttonContainer: {
paddingVertical: 15,
marginTop: 20,
backgroundColor: '#2c3e50',
borderRadius: 15
},
buttonText: {
textAlign: 'center',
color: '#ecf0f1',
fontWeight: '700'
},
});
Current behaviour is MyModal
ponent being displayed when I access to MyList
ponent for the first time, I can close it and then the FlatList
is there but when a list item is pressed, MyModal
ponent is not displayed.
How can I manage to hide Modal and open it only when a list item is pressed?
Another doubt related to this is:
How to pass the pressed item object to MyModal
ponent?
Thanks in advance!
Share Improve this question edited Nov 3, 2017 at 10:27 juankysmith asked Oct 30, 2017 at 12:27 juankysmithjuankysmith 12.5k6 gold badges39 silver badges65 bronze badges 3-
The first part may just be a typo - in
MyList._onPressItem
you're not actually calling_showModal
(missing the brackets). – Rob Hogan Commented Oct 30, 2017 at 13:08 -
One other quick tip -
FlatList
is a scrolling view in itself - there's no need to wrap it in aScrollView
. – Rob Hogan Commented Oct 30, 2017 at 17:08 - Thank you so much I removed the ScrollView and it works perfect, I am stuck trying to access the selectedItem in MyModal ponent, as I wrote in the ment to your answer, any sugestions? – juankysmith Commented Oct 30, 2017 at 17:18
2 Answers
Reset to default 4Passing the item
through to the modal
To pass the selected item to your modal, you'll need to add it as a prop
on your Modal
ponent.
You can remember the selected item in MyList
's state:
_onPressItem = (item) => {
this._showModal(item);
};
_showModal = (selectedItem) => this.setState({ isModalVisible: true, selectedItem })
And then pass it through to the modal when you render
it from MyList
:
// ...
</ScrollView>
<MyModal
modalVisible={this.state.isModalVisible}
selectedItem={this.state.selectedItem} />
</KeyboardAvoidingView>
// ...
Controlling the modal's visibility
Currently you have a modal visibility boolean in both MyList
's state (isModalVisible
, which is passed through to MyModal
as the modalVisible
prop) and also in MyModal
's state (modalVisible
). There's no need for the last one - it'll just give you a headache trying to keep them in sync. Just use the props to "control" MyModal
, keeping a single source of truth, and pass a callback to allow MyModal
to tell MyList
that the modal should be dismissed.
// ...
</ScrollView>
<MyModal
modalVisible={this.state.isModalVisible}
selectedItem={this.state.selectedItem}
onDismiss={this._hideModal} />
</KeyboardAvoidingView>
// ...
A new stateless MyModal
:
export default class MyModal extends Component {
render() {
return (
<View>
<Modal
animationType="slide"
transparent={false}
visible={this.props.modalVisible}
onRequestClose={() => { this.props.onDismiss() }}
>
<View style={styles.container}>
<View style={styles.innerContainer}>
<Text>Item Detail</Text>
<TouchableHighlight
style={styles.buttonContainer}
onPress={() => { this.props.onDismiss() }}>
<Text style={styles.buttonText}>Close</Text>
</TouchableHighlight>
</View>
</View>
</Modal>
</View>
);
}
}
class MyItem extends React.Component {
_onPress = () => {
this.props.onPressItem(this.props.item);
};
render() {
return(
<TouchableOpacity
{...this.props}
onPress={this._onPress}
>
<Text style={styles.itemText}> {this.props.item.name}</Text>
</TouchableOpacity>
)
}
}
export default class MyList extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
data: {}, // some data correctly loaded
isModalVisible: false,
selectedItem: null
};
};
_onPressItem = (item) => {
this._showModal(item);
};
_hideMyModal = () => {
this.setState({isModalVisible: false})
}
_showModal = (item) => this.setState({ isModalVisible: true,
selectedItem: item })
_keyExtractor = (item, index) => item.id;
_renderItem = ({item}) => (
<MyItem
style={styles.row}
item={item}
onPressItem={() => this._onPressItem(item)}
/>
);
render() {
return(
<KeyboardAvoidingView behavior="padding" style={styles.container}>
<View style={styles.titleContainer}>
<Text style={styles.title}>Tittle</Text>
</View>
<ScrollView style={styles.container}>
<FlatList
data={this.state.data}
ItemSeparatorComponent = {this._flatListItemSeparator}
renderItem={this._renderItem}
keyExtractor={this._keyExtractor}
/>
</ScrollView>
{ this.state.isModalVisible && <MyModal selectedItem={this.state.selectedItem} modalVisible={this.state.isModalVisible} hideModal={this_hideMyModal} /> }
</KeyboardAvoidingView>
);
}
}
When you press the item in FlatList, just set it in the state and pass it as a prop to MyModal ponent(pretty self explanatory).
export default class MyModal extends Component {
constructor(props) {
super(props);
this.state = {
isModalVisible: props.modalVisible
};
};
_setModalVisible(visible) {
this.setState({modalVisible: visible});
}
render() {
return (
<View>
<Modal
animationType="slide"
transparent={false}
visible={this.state.isModalVisible}
onRequestClose={() => {alert("Modal has been closed.")}}
>
<View style={styles.container}>
<View style={styles.innerContainer}>
<Text>Item Detail</Text>
<TouchableHighlight
style={styles.buttonContainer}
onPress={() => { this.props.hideModal() }}>
<Text style={styles.buttonText}>Close</Text>
</TouchableHighlight>
</View>
</View>
</Modal>
</View>
);
}
}
In your previous code in MyModal
you were doing this --> onPress={() => { this._setModalVisible(false) }}>
which will hide the modal in the MyModal
class but MyModal
is still rendered in the MyList
class. So you might be unable to click the FlatList item again. I passed a prop hideModal
which will remove the MyModal
ponent from the MyList
. In my revised code you don't need to pass modalVisible
prop also, but its up to you for that.