Pass parameters with props in react native - react-native

I want to pass the clicked searchItem's id from my component. My code is as follows.
const searchResultList = props => (
<View style={styles.container}>
<ScrollView keyboardShouldPersistTaps="always">
{props.itemList.map(item => (
<SearchResultListItem
key={item.id}
imageSource={item.workoutImage}
mainText={item.workoutName}
subText={item.length + " | " + item.difficulty}
onItemPressed={props.onItemPressed}
/>
))}
</ScrollView>
</View>
);
When, onItemPressed, i want to pass the item.id with the prop.onItemPressed. This is my screen.
<SearchResultList
itemList={this.props.searchedWorkouts}
onItemPressed={id => alert(item.id)} //this.onLoadWorkoutDetailView()}
/>
How can I achieve this? I want the clicked itemId to my main screen.

You need to make an addition function. Straightforward it will be like this:
...
onItemPressed={() => props.onItemPressed(item.id)}
...
But, it`s not a good solution from performance point of view.
The better way is to make a method and bind all items to this method.
class SearchResultList extends React.PureComponent {
constructor(props) {
super(props);
const {itemsList} = props;
this._onPressHandlers = {};
for (let {id} for itemsList) {
this._onPressHandlers[id] = this._onItemPress.bind(this, id);
}
}
_onItemPress(id) {
return this.props.onItemPressed(id);
}
render() {
return (
<View style={styles.container}>
<ScrollView keyboardShouldPersistTaps="always">
{props.itemList.map(item => (
<SearchResultListItem
key={item.id}
imageSource={item.workoutImage}
mainText={item.workoutName}
subText={item.length + " | " + item.difficulty}
onItemPressed={this._onPressHandlers[item.id]}
/>
))}
</ScrollView>
</View>
);
}
}
In this case the handlers will not be generated on each render of list and will not cause re-render of SearchResultListItem component.
Obviously, if you expect changes of the itemsList prop, you will need to rebind it on componendWillReceiveProps method.

Related

FlatList not rendering style dynamically

I'm currently struggling in making my FlatList applying the changes I do to it. What I am wanting right now is that when I click an item in my flatlist, that it highlights in a certain color. I followed an approach done by a guy but I am having the problem that to me is not working the update once I click.
I can see through console that all I am doing performs a modification but I think that I am missing some point with extraData parameter since it is not re-rendering with the backgroundColor that I would like to apply.
The code I have is as following, I know that the style I am applying is correct since if i substitute in the map styles.list per styles.selected, everything gets the background I would like to be applied to the elements I click.
So summarizing, the issue I think I have is that the flatlist is not re-rendering so it doesn't show the modifications I perform on it. Any idea of what I am doing wrong? Any tip?
render() {
const { students, studentsDataSource, loading, userProfile } = this.props.navigation.state.params.store;
this.state.dataSource = studentsDataSource._dataBlob.s1.map(item => {
item.isSelect = false;
item.selectedClass = styles.list;
return item;
})
const itemNumber = this.state.dataSource.filter(item => item.isSelect).length;
return (
<View style={styles.container}>
<Item rounded style={styles.searchBar}>
<Input placeholder='Group Name'/>
</Item>
<FlatList
style={{
flex: 1,
width: "100%",
}}
data={this.state.dataSource}
ItemSeparatorComponent={this.FlatListItemSeparator}
renderItem={ ({ item }) => (
<ListItem avatar style={[styles.list, item.selectedClass]}
onPress={() => this.selectItem(item)}>
<Left>
{!item.voteCount && <Avatar unseen={true} /> }
{!!item.voteCount > 0 && <Avatar />}
</Left>
<Body>
<Text>{item.name}</Text>
<Text note>{item.group}</Text>
</Body>
</ListItem>
)
}
listKey={item => item.key}
extraData={this.state}
/>
</View>
);
}
Here we can find the state and SelectItem functions:
constructor(props) {
super(props)
this.state = {
dataSource : [],
}
}
//FlatListItemSeparator = () => <View style={styles.line} />;
selectItem = data => {
//{console.log("inside SelectItem=", data)}
data.isSelect = !data.isSelect;
data.selectedClass = data.isSelect? styles.selected: styles.list;
const index = this.state.dataSource.findIndex( item => data.key === item.key);
this.state.dataSource[index] = data;
this.setState({
dataSource: this.state.dataSource,
});
console.log("This state has the changes:=",this.state.dataSource)
};
Well the main issue was that I was not using the .setState and instead I was doing assignations which killed the listeners.

Best method to optimize performance of FlatList Items

This a simple FlatList:
class Products ..
render() {
return (
<FlatList
renderItem={this._renderItem}
);
}
I want to create a list of items and navigate to Detail Page by onPress items.
Can Please tell me which method is better?
Method 1:
Insert navigate to Detail page in child component(CardProduct component) like this:
_renderItem = ({item}) => (
<CardProduct
id={item.id}
title={item.title}
/>
);
and in CardProduct component:
render() {
const { id,title } = this.props;
return (
<Card style={{flex:1}}>
<CardItem cardBody button onPress={() => this.props.navigation.navigate('Details',{productId:id})}>
...
);
}
Method 2:
Insert navigate to Detail page in current component(Products component) like this:
_onPressItem = (id: string) => {
this.props.navigation.navigate('Details',{productId:id});
};
_renderItem = ({item}) => (
<CardProduct
id={item.id}
title={item.title}
onPressItem={this._onPressItem}
/>
);
and in CardProduct component:
_onPress = () => {
this.props.onPressItem(this.props.id);
};
render() {
const { id,title } = this.props;
return (
<Card style={{flex:1}}>
<CardItem cardBody button onPress={this._onPress}>
...
);
}
I used to do the method 1, but I read this guide.
Short answer:
You should go for method2.
Explanation:
In method1 you are using an arrow function in CardItem's onPress, so everytime CardProduct is re-rendered a new reference of onPress is created, which forces CardItem to re-render, even if all the other props are staying the same. In method 2 you are binding the function to context, which won't force a re-rendering of the CardItem.
By the way, in general it is a good idea to prevent the usage of arrow functions in render().
One step for performance optimization in react-native flatlist, is using a stateless functional component for the renderItem. and you should always give each item a unique key.

undefined is not a function in TouchableOpacity onPress

The question is almost similar to this one :
touchableopacity onpress function undefined (is not a function) React Native
But the problem is, I am getting the error despite the fact that I have bind the function. Here is my TouchableOpacity component:
<TouchableOpacity style={styles.eachChannelViewStyle} onPress={() => this.setModalVisible(true)}>
{item.item.thumbnail ?
<Image style={styles.everyVideoChannelThumbnailStyle} source={{uri: item.item.thumbnail}} />
: <ActivityIndicator style= {styles.loadingButton} size="large" color="#0000ff" />}
<Text numberOfLines={2} style={styles.everyVideoChannelVideoNameStyle}>
{item.item.title}
</Text>
</TouchableOpacity>
And this is my setModalVisible function:
setModalVisible(visible) {
console.error(" I am in set modal section ")
this.setState({youtubeModalVisible: visible});
}
Also, I have bind the function in constructor as follows:
this.setModalVisible = this.setModalVisible.bind(this);
But, I am still getting same error that undefined is not a function. Any help regarding this error?
The render method and your custom method must be under the same scope. In code below I have demonstrated the same. I hope you will modify your code accordingly as I assume you got the gist :)
class Demo extends Component {
onButtonPress() {
console.log("click");
}
render() {
return (
<View>
<TouchableOpacity onPress={this.onButtonPress.bind(this)}>
<Text> Click Me </Text>
</TouchableOpacity >
<View>
);
}
}
Alternatively binding method in constructor will also work
class Demo extends Component {
constructor(props){
super(props);
this.onButtonPress= this.onButtonPress.bind(this);
}
onButtonPress() {
console.log("click");
}
render() {
return (
<View>
<TouchableOpacity onPress={this.onButtonPress()}>
<Text> Click Me </Text>
</TouchableOpacity >
<View>
);
}
}
I'm not sure if this will help but I write my functions this way and haven't encountered this problem.
If I were you I'd try binding the function in the place where you declare it.
setModalVisible = (visible) => {
this.setState({ youtubeModalVisible: visible });
}
If you do this, you don't have to bind in the constructor.
constructor(props) {
...
// Comment this out to see it will still bind.
// this.setModalVisible = this.setModalVisible.bind(this);
...
}
Lastly, if this function will only set the modal's state to visible, you might want to remove the argument and pass it this way.
<TouchableOpacity style={styles.eachChannelViewStyle} onPress={this.setModalVisible}>
...
</TouchableOpacity>
// Refactored function declaration would look like this
setModalVisible = () => {
this.setState({ youtubeModalVisible: true });
}

Send data back from component in react native

I want to filter my list of data but I want to keep my filter layout seperate to avoid having thousand lines of code... How can I send the filter options back to my main view?
I know I can add parameters to the <Filter /> component, but I need to find a way to send them back to the main screen..
Imagine my main.js screen looking like this:
<View>
<ListView dataSource={this.state.dataSource} renderRow={this.renderListItem}></ListView>
<Filter />
</View>
filter.js screen:
<View>
<Input>filter text here</Input>
</View>
Note: this is a minimized version
Taking the concepts from Thinking in React, this is what you should do:
class MyComponent extends Component {
state = {
filter1: '',
filter2: '',
// Other stuff
};
handleFilter1Change = (filterText) => {
// Update filter1 and dataSource
};
handleFilter2Change = (filterText) => {
// Update filter2 and dataSource
};
render() {
return (
<View>
<ListView dataSource={this.state.dataSource} renderRow={this.renderListItem}></ListView>
<Filter
filter1Value={this.state.filter1}
filter2Value={this.state.filter2}
onFilter1Change={this.handleFilter1Change}
onFilter2Change={this.handleFilter2Change} />
</View>
);
}
}
const Filter = ({ filter1, filter2, onFilter1Change, onFilter2Change }) => {
return (
<View>
<TextInput
value={filter1}
onChangeText={onFilter1Change} />
<TextInput
value={filter2}
onChangeText={onFilter2Change} />
</View>
);
}
Essentially, the rule of thumb is to send a parent function down, which the child will call, passing data to it.

How can I add events in child component in listview? Is it possible to add event in listview?

' In My listview two component:first,second.
In the second there are map function return third (array elements).
How can I pass and the click event in the first, second, third & inner. For comments & sub comments in post. In the bellow updateComments is not working. And one thing, WallUpdates is also child component.'
class WallUpdates extends Component {
constructor(props) {
super(props);
this.updateComments = this.updateComments.bind(this);
}
updateComments(){
alert("updatecommentPost");
}
render() {
return(
<View>
<Text>
{this.props.allPost}
</Text>
<ListView
dataSource={this.props.dataSource} renderRow={this.renderMovie} style={styles.listView}
/>
</View>
);
}
renderMovie(list) {
var commentNodes = list.comment.map((item, i) => {
console.log(item);
console.log(item.commentcontent);
return (<Comments key={item.cid} comment={item.commentcontent} />);
});
//
return (
<View style={styles.container} >
<Text style={styles.year}>{list.postcontent + '\n\n'}</Text>
<TouchableOpacity onPress={this.updateComments}>
<View style={styles.buttonsmall}>
<Text style={styles.buttonsmallText}>comments!</Text>
</View>
</TouchableOpacity>
{commentNodes}
</View>
);
}
}
I have changed - renderMovie(list) to renderMovie = (list) => and also change onPress={this.updateComments} to onPress={() => { this.updateMe() }} & this is working for me .