How to pass props from FlatList item to Modal? - javascript

I have implemented a View component containing a FlatList, which renders TouchableHighlights. Also I have implemented a Modal component, which I'd like to import at various places including the component that renders the FlatList.
I have already managed to open the modal from outside (via handing over a prop for visibility, accessing it via nextProps and setting modals state value "modalVisible" to this) and closing it from inside (simply via changing it's state value "modalVisible").
BUT: I also want to pass data to the modal from each FlatLists item. An item rendered as a TouchableHighlight should open the modal onPress and the modal should contain data from the item (in the code below this would be the items id). How can I achieve passing data to the modal? I somehow can't get it to work using nextProps. This seems more to be an issue related to setting state from within a FlatLists item rather than the Modal.
Modal:
export default class ModalView extends React.Component {
constructor() {
super();
this.state = {
modalVisible: false,
id: null,
};
}
componentWillReceiveProps(nextProps) {
this.setState({
modalVisible: nextProps.modalVisible,
id: nextProps.id,
})
}
render() {
return (
<Modal
animationType="slide"
transparent={ true }
visible={ this.state.modalVisible }
onRequestClose={() => { this.props.setModalVisible(false) }}
>
<View>
<View>
<Text>{ this.state.id }</Text>
<TouchableHighlight
onPress={() => { this.props.setModalVisible(false) }}
>
<Text>Hide Modal</Text>
</TouchableHighlight>
</View>
</View>
</Modal>
)
}
}
FlatList rendering TouchableHighlights:
export default class RecentList extends React.Component {
constructor() {
super();
this.state = {
modalVisible: false,
id: null,
}
}
_onPressItem(id) {
this.setState({
modalVisible: true,
id: id,
});
};
_renderItem = ({item}) => {
return (
<TouchableHighlight
id={item.id}
onPress={this._onPressItem}
>
<View>
<Text>{id}</Text>
</View>
</TouchableHighlight>
)
};
render() {
let data = realm.objects('Example').filtered('completed = true')
.sorted('startedAt', true).slice(0, 10)
return (
<View>
<ModalView
modalVisible={ this.state.modalVisible }
setModalVisible={ (vis) => { this.setModalVisible(vis) }}
id={ this.state.id }
/>
<FlatList
data={data}
renderItem={this._renderItem}
keyExtractor={(item, index) => index}
/>
</View>
)
}
}

A small mistake you have missed ...
_renderItem = ({item}) => {
return (
<TouchableHighlight
id={item.id}
onPress={() => this._onPressItem(item.id)} // Your not sending the item.id
>
<View>
<Text>{id}</Text>
</View>
</TouchableHighlight>
)
};

Related

How can I pass a selected object from an array (firestore) to another class in react native?

I have a question about passing specific Objects from an array to another class in react native. If I press the button, all objects go to another class instead of the one I need, but I want the one which I pressed. Do I have to create a function for the selected object/ key or is it enough if I change something in the onPress={} method?
I need to change something here -> TouchableOpacity onPress={()=>this.props.navigation.navigate('detail', item,{})}> Item must be the document (firestore) with all the information I have, but how can I pass the object from the array only?
Here are also some pictures for a better understanding, thank you in advance
export default class foods extends Component {
constructor(props) {
super(props);
this.state = {
posts:[],
}
}
componentDidMount() {
StatusBar.setHidden(true)
this.subscriber = database.collection('users').onSnapshot(docs => {
let posts = []
docs.forEach(document => {
posts.push({
id: document.id,
...document.data(),
})
})
this.setState({posts})
console.log(posts)
})
}
render() {
return (
<ScrollView>
<View style={styles.container}>
{
this.state.posts.map((item) => {
return (
<View >
{
Object.keys(item.post).map((key) => {
return (
<View style={styles.card}>
<TouchableOpacity onPress={() => this.props.navigation.navigate('detail', item, {})}>
<Text>{item.firstName} {item.lastName}</Text>
<Image style={styles.cardImage} source={{ uri: item.post[key].image }} />
<Text>{item.post[key].essenname}</Text>
</TouchableOpacity>
</View>
)
})
}
</View>
)
})
}
</View>
</ScrollView>
);
}
}
export default class detail extends Component {
constructor(props) {
super(props);
this.state = {
post: [this.props.route.params]
}
};
componentDidMount() {
StatusBar.setHidden(true)
console.log('Array : ', this.state.post)
}
render() {
return (
<View style={styles.container}>
<ScrollView>
{this.state.post.map((item) => {
return (
Object.keys(item.post).map((key) => {
return (
<View style={styles.card}>
<Image style={styles.cardImage} source={{ uri: item.post[key].image }} />
<Text>{item.post[key].essenname}</Text>
</View>
)
})
)
})}
</ScrollView>
</View>
);
}
}
I want to pass either Post[0] or Post[1] but not both:
![](https://i.stack.imgur.com/3recX.png)
This is my output, which is the same output after navigating the item to the detail screen:
![](https://i.stack.imgur.com/WMqrd.png)
Detail screen on the right:
![](https://i.stack.imgur.com/gIX0V.png)
The document with the array 'post':
![](https://i.stack.imgur.com/dO1uQ.png)
There is no need to use Object.keys to loop trought your posts, just use item.post.
In your navigation params, just use a spred operator for the root key and pass the post selected: this.props.navigation.navigate('detail', {posts: {...this.state.posts, post: [p]}}).
In your detail component state your are missing to access the params, it should be:
this.state={
post:[this.props.route.params.posts]
}
and replace Object.keys with item.post

Can we use this.props.map as well as this.props.navigation in single class to navigate to another screen

I have used this.props.maps as well as this.props.navigation which is showing an error:
this.props.navigation.navigate is undefined object
Trying to navigate to another page by rendering the firebase database but getting error but the same code i tried by simple creating a view and navigating to another page then it is working
export default class ItemComponent extends Component {
constructor(props) {
super(props);
// need to bind `this` to access props in handler
this._onEditLibrary = this._onEditLibrary.bind(this);
}
static propTypes = {
items: PropTypes.array.isRequired
};
_onEditLibrary=()=> {
this.props.navigation.navigate('EditLibrary');
};
render() {
return (
<View style={styles.itemsList}>
<TouchableOpacity onPress={this._onEditLibrary}>
{this.props.items.map((item, index) => {
return (
<View key={index}>
<ImageBackground source={item.Image} style={ { height:150, width:150}}>
<Text style={styles.itemtext}>{item.Name}</Text>
</ImageBackground>
</View>
)
})
}
</TouchableOpacity>
</View>
);
}
}
Need to navigate to another page
Try this out
export default class ItemComponent extends Component {
constructor(props) {
super(props);
// need to bind `this` to access props in handler
this._onEditLibrary = this._onEditLibrary.bind(this);
}
static propTypes = {
items: PropTypes.array.isRequired
};
_onEditLibrary=()=> {
this.props.navigation.navigate('EditLibrary');
};
render() {
return (
<View style={styles.itemsList}>
{this.props.items.map((item, index) => {
return (
<TouchableOpacity key={index} onPress={this._onEditLibrary}>
<ImageBackground source={item.Image} style={ { height:150, width:150}}>
<Text style={styles.itemtext}>{item.Name}</Text>
</ImageBackground>
</TouchableOpacity>
)
})
}
</View>
);
}
}

How to pass a value to modal and show modal when click on flatlist item in react native

i want to pass a value from activity screen to modal screen. I am trying to open a screen when click on flatlist item an dpass value of item to modal,but it shows my detail before rendering modal screen,Here is my activity screen:-
<FlatList
data={this.state.myListDataSource}
renderItem={this._renderItem}
showsHorizontalScrollIndicator={false}
showsVerticalScrollIndicator={false}
keyExtractor={({item,index}) => item+index}
refreshControl={
<RefreshControl
refreshing={this.state.isRefreshing}
onRefresh={this.pullToRefresh}
/>
}
/>
<ListProducts
modalVisibility={this.state.listModalVisibility}
closeModal={() =>
this.setState({listModalVisibility:false})}
listName={this.state.listName}
listId={this.state.listId}
/>
handleListItemPress = (item) => {
this.setState({
listModalVisibility:true,
listName : item.name,
listId : item.list_id
})
showMessage('List Item : '+item.listId)
}
_renderItem = ({item}) => {
return(
<TouchableOpacity onPress={() => this.handleListItemPress(item)}>
<View >
<View>
<View style={{flexDirection:'row',marginBottom:2}}>
<ImageView
image={item.pictures[0]}
style={[{marginRight:2},styles.imageStyle]}
/>
<ImageView
image={item.pictures[1]}
style={[{marginLeft:2},styles.imageStyle]}
/>
</View>
<View style={{flexDirection:'row',marginTop:2}}>
<ImageView
style={[{marginRight:2},styles.imageStyle]}
image={item.pictures[2]}
/>
<ImageView
image={item.pictures[3]}
style={[{marginLeft:2},styles.imageStyle]}
/>
</View>
</View>
<View>
<TextViewNonClickable
textViewText={item.name}
/>
<TextViewNonClickable
textViewText={item.description}
/>
</View>
<Icon
name = 'more-vertical'
type = 'feather'
color = {color.colorWhite}
iconStyle={{padding:8}}
containerStyle = {{position:'absolute',top:8,right:8}}
onPress = {() => {
showMessage('Options')
}}
/>
</View>
</TouchableOpacity>
)}
This is my modal screen where i want to get list item id or name.But that screen shows details on the screen rather than rendering modal screen .
Here is my modal screen :-
export default class ListProducts extends Component {
constructor(props){
super(props)
this.state={
products : [],
refreshing : false,
listId : 256,
listName : props.name
}
}
_renderProducts = ({item}) => {
return(
<Product
image={item.image}
name={item.name}
price={item.price}
discountedPrice={item.discounted_price}
quantityAdded={item.quantity_added}
productId={item.product_id}
stock={item.stock}
/>
)
}
render() {
const {modalVisibility,closeModal,listName,listId} = this.props;
return (
<Modal
animationIn='bounceInRight'
animationOut='bounceOutRight'
isVisible={modalVisibility}
onBackButtonPress={closeModal}
>
<View style={{flex:1,backgroundColor:color.colorWhite}}>
<Header
placement='left'
leftComponent={
<FlatList
data={this.state.products}
renderItem={this._renderProducts}
keyExtractor={(item,index) => item+index}
refreshControl={
<RefreshControl
refreshing={this.state.refreshing}
onRefresh={this.pullToRefresh}
/>
}
numColumns={3}
style={{paddingLeft:2,paddingRight:2}}
/>
</View>
</Modal>
)
}
}
Step 1: pass props from modal to class.
In modal like:
this.props.setItem(“sunny”)
Step 2: Get that props in class in render method where modal initialised.
<ModalName SetItem={item => console.log(item)} \>
I'm actually using 'Dialog' from 'react-native-simple-dialogs' for popup. It works better for me than 'Modal', but i think the logic is the same.
Here is a simplified example of a edit email popup that works for me:
import React from 'react';
import { StyleSheet, View, TextInput } from 'react-native';
import { Button, Text } from 'native-base';
import { Dialog } from 'react-native-simple-dialogs';
export default class EditEmailPopup extends React.Component {
constructor(props) {
super(props);
this.state = {
isModalVisible: this.props.isModalVisible,
};
}
componentWillUpdate() {
this.state.isModalVisible = this.props.isModalVisible;
}
_updateEmailAndCloseDialog() {
// update some data...
this._onCloseDialog();
}
_onCloseDialog() {
this.setState({ isModalVisible: false});
this.props.client(); //this is a function transfered from parent that controls the visibility of the dialog.
}
render() {
return (
<View>
<Dialog
visible={this.state.isModalVisible}
onTouchOutside={() => this._onCloseDialog()}
>
<View>
<Text style={styles.text}>{'Update Email text'}</Text>
<View style={styles.popupButtons}>
<Button
transparent
style={styles.cancelButton}
onPress={() => this._onCloseDialog()}
>
<Text> {'cancel'} </Text>
</Button>
<Button
style={styles.okButton}
onPress={() => this._updateEmailAndCloseDialog()}
>
<Text> {'ok'} </Text>
</Button>
</View>
</View>
</Dialog>
</View>
);
}
}
Here is how i add my Dialog in the parent view:
{this.state.emailModalVisibility ? (
<EditEmailPopup
isModalVisible={this.state.emailModalVisibility}
client={this.afterClosePopup}
/>
) : null}
while this.state.emailModalVisibility initiated in the constructor in state as 'false'.
function written in parent:
_afterClosePopup = () => {
this.setState({
emailModalVisibility: false
});
};
and binded in the constructor so 'this' will belong to parent:
constructor(props) {
super(props);
this.afterClosePopup = this._afterClosePopup.bind(this);
this.state = {
emailModalVisibility: false
};
}

How to call a child method from the parent in React Native?

When a click event is fired within my parent component I need to call the method closeMenu() from the SearchBar child component. I have tried a couple of different ways to do that but none of them are working. Does anyone know how to do this?
class Products extends Component {
constructor(props) {
super(props);
this.state = { closeMenu: false};
this.hideSearchBar = this.hideSearchBar.bind(this);
}
hideSearchBar(e) {
console.log('e: ', React.Children)
this.setState({closeMenu: true});
this.refs.SearchBar.closeMenu();
this.setState({closeMenu: false});
}
componentWillMount() {
this.props.dispatch(getProductList());
}
render() {
const {isLoading, products} = this.props.products;
if (isLoading) {
return <Loader isVisible={true}/>;
}
return (
<TouchableWithoutFeedback onPress={(e) => this.hideSearchBar(e)} style={{zIndex: 0}}>
<View style={styles.wrapper}>
<Header/>
<View style={styles.bodyWrapper}>
<ScrollView style={styles.scrollView}>
<ProductsContainer data={{productsList: { results: products }}}/>
</ScrollView>
<SearchBar ref="SearchBar" style={styles.searchBar} />
</View>
<Footer/>
</View>
</TouchableWithoutFeedback>
);
}
}
I also tried calling closeMenu() without refs:
hideSearchBar(e) {
this.setState({closeMenu: true});
this.SearchBar.closeMenu();
}
Here is the SearchBar component:
class SearchBar extends Component {
constructor(props) {
super(props);
}
componentDidMount() {
this.suggestions = [];
}
componentWillUpdate(nextProps, nextState) {
console.log("COMPONENT WILL UPDATE");
console.log(nextProps);
console.log(nextState);
}
suggestionClick = (value) => {
}
getSuggestionText = (suggestion) => {
}
onChangeText = (value) => {
this.selectedSuggestion = false
this.props.dispatch(handleSearchItemText(value));
console.log(this.props.products.searchResults);
}
onFocus() {
const {height} = Dimensions.get('window');
this.setState({
contentOffset: {
x: 0,
y: height * 1 / 3
}
});
}
onBlur() {
this.setState({
contentOffset: {x: 0, y: 0}
});
}
closeMenu = () => {
this.props.products.searchResults = {};
}
componentWillReceiveProps() {
if (!this.props.closeMenu) {
this.props.closeMenu = false;
}
}
renderSearches = () => {
this.suggestions = this.props.products.searchResults;
const suggestionTexts = Object.keys(this.props.products.searchResults || {})
console.log(suggestionTexts);
if (!suggestionTexts.length) {
return null
}
// for android absolute element: https://github.com/facebook/react-native/issues/16951
// https://gist.github.com/tioback/6af21db0685cd3b1de28b84981f31cca#file-input-with-suggestions-L54
return (
<View
ref="suggestionsWrapper"
style={autoStyles.suggestionsWrapper}
>
{
this.suggestions.map((text, index) => (
<TouchableHighlight
key={index}
suggestionText={text}
activeOpacity={0.6}
style={autoStyles.suggestion}
onPress={this.suggestionClick(this.suggestions[text])}
underlayColor='white'
>
<Text style={autoStyles.suggestionText}>
{text}
</Text>
</TouchableHighlight>
))
}
</View>
)
}
render() {
const myIcon = (<Icon name="search" size={30} style={styles.searchIcon}/>);
const slidersIcon = (<Icon name="sliders" size={30} style={styles.slidersIcon}/>);
return (
<TouchableWithoutFeedback style={{zIndex: 0}}>
<View style={[styles.searchBar, this.props.style]}>
<View style={styles.searchContainer}>
<View>
{slidersIcon}
</View>
<View style={styles.search}>
<View style={styles.searchSection}>
{myIcon}
<TextInput
style={styles.input}
placeholder="Search"
placeholderTextColor="rgba(0,0,0,0.7)"
onChangeText={(searchString) => {
this.setState({searchString})
}}
underlineColorAndroid="transparent"
editable={true}
autoCorrect={false}
autoFocus={false}
autoCaptialize={'none'}
autoCorrect={false}
onChangeText={this.onChangeText}
enablesReturnKeyAutomatically={true}
onFocus={() => this.onFocus()}
onBlur={() => this.onBlur()}
/>
</View>
</View>
</View>
{this.renderSearches()}
</View>
</TouchableWithoutFeedback>
);
}
}
There are some issues which you should avoid:
Never mutate props: this.props.something = {} is an anti-pattern. Think about props as data that your component does not own and which are not mutable. If they change then only because the parent passed new props.
Also you have multiple handlers in your SeachBar that are not bound to this but use this. It will not work. Use arrow functions if you want to use this or bind them in the constructor.
You should overthink the architecture of your app. Maybe it is a good idea to split the search bar and the result list into two separate components. When the user types something to search for update your redux store and display the results in a separate component that you only render if there are search results.
I'm affraid it would exceed the length of a stackoverflow answer to solve all these issues. Maybe you should go back to the basics first and do the really good redux tutorial.

React native. losing context of this in touchable highlight on press.

I'm trying to add a TouchableHighlight component to a row in a list view.
The onPress function is throwing an undefined error in the code below. It works outside of the list view.
I suspect this is because I'm losing context of this but unsure how to fix. Anyone able to help?
export default class ConversationsList extends Component {
constructor(props) {
super(props);
this._handleChangePage = this._handleChangePage.bind(this);
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.state = {
dataSource: ds.cloneWithRows(Coversations.chats)
};
}
_handleChangePage(chat) {
this.props.navigator.push({
title: 'foo',
component: Chat,
passProps: {
chat: chat
}
});
}
renderRow(chat){
return (
<View>
<TouchableHighlight onPress={ () => this._handleChangePage.bind(this, chat) }>
<View>
/* more content removed */
</View>
</TouchableHighlight>
</View>
);
}
render() {
return (
<View style={styles.container}>
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderRow}
/>
</View>
);
}
}
I also suspect that I shouldn't really be doing things this way, that my component should be structured differently, so it is passed the press handler as a prop perhaps. any advice appreciated.
You can declare another variable globally like
var _this;
initialise it in render method
render:function(){
_this = this;
return(
...
)
}
and use it in your touchableHightlight
renderRow(chat){
return (
<View>
<TouchableHighlight onPress={ () => _this._handleChangePage(chat) }>
<View>
/* more content removed */
</View>
</TouchableHighlight>
</View>
);
}
I suggest to read this helpful article
export default class ConversationsList extends Component {
constructor(props) {
super(props);
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.state = {
dataSource: ds.cloneWithRows(Coversations.chats)
};
}
_handleChangePage = () => {
this.props.navigator.push({
title: 'foo',
component: Chat,
passProps: {
chat: this
}
});
}
renderRow = (chat) => {
return (
<View>
<TouchableHighlight onPress={ this._handleChangePage }>
<View>
/* more content removed */
</View>
</TouchableHighlight>
</View>
);
}
render() {
return (
<View style={styles.container}>
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderRow}
/>
</View>
);
}
}

Categories

Resources