TouchableOpacity onPress hangs inside ListView - javascript

I have this ListView UI that started to run slowly once I added the rowData parameter inside the onPress event handler of TouchableOpacity. Once the TouchableOpacity is pressed, it stays pressed for 15 seconds and then again runs smoothly.
There seems to be some collision, because I use rowData also in the renderRow event handler of ListView three lines above.
Am I right and how to solve this problem?
<ListView
dataSource={this.state.dataSource}
keyboardShouldPersistTaps={true}
renderRow={(rowData) =>
<TouchableOpacity
onPress={(rowData) => {
console.log(rowData);//ON THIS LINE IT HANGS 15s
}}
>
<Text>{rowData}</Text>
</TouchableOpacity>
}
automaticallyAdjustContentInsets={false}
/>

I'd be really interested in the explanation of what makes this such an expensive operation in javascript, but the problem is that you're passing rowData as an argument to your onPress function, when rowData is already declared in the upper scope (renderRow). So yes, just like you said, there's a collision.
Effectively, the value of rowData is being redefined by onPress, since the onPress function receives the touch event as an argument. (You'll notice that the data being logged isn't actually your original row data, but a touch event object).
You can fix this by simply renaming the first argument of your onPress function. e.g.
<TouchableOpacity
onPress={(evt) => {
console.log(rowData); //now it doesn't hang
}}
>

Can we put header with Webview ? like
render() { // eslint-disable-line class-methods-use-this
return (
<Container theme={theme} style={{ backgroundColor: theme.defaultBackgroundColor }}>
<Header style={{ justifyContent: 'flex-start', paddingTop: (Platform.OS === 'ios') ? 23 : 9 }}>
<Button transparent onPress={() => this.popRoute()} >
<Icon name="ios-arrow-round-back-outline" style={{ fontSize: 30, lineHeight: 32, paddingRight: 10 }} />
Find Stores
</Button>
</Header>
<WebView
ref={WEBVIEW_REF}
automaticallyAdjustContentInsets={false}
source={{uri: this.state.url}}
javaScriptEnabled={true}
domStorageEnabled={true}
decelerationRate="normal"
onNavigationStateChange={this.onNavigationStateChange}
onShouldStartLoadWithRequest={this.onShouldStartLoadWithRequest}
startInLoadingState={true}
scalesPageToFit={this.state.scalesPageToFit}
/>
</Container>
);

Related

React has detected a change in the order of Hooks?

I keep getting this error saying "ERROR Warning: React has detected a change in the order of Hooks called by MainMenuScreen. This will lead to bugs and errors if not fixed. For more information, read the Rules of Hooks: https://reactjs.org/link/rules-of-hooks". Really confused don't know why am getting this error if someone can please explain or show an example it would greatly be appreciated. Many thanks for considering my request.
Here is an image of the code.
function MainMenuScreen({ navigation, route, props }) {
const globalContext = useContext(Context)
const { setIsLoggedIn, appSettings, domain, userObj, setUserObj, setToken, address, setAddress } = globalContext;
const [selecedTab, setSelectedTab] = React.useState(tabs[0]);
return (
<View style={styles.container}>
<LinearGradient colors={['gold', '#FF7F50', '#FF7F50']} style={StyleSheet.absoluteFill}>
<FlatList
data={tabs}
horizontal
showsHorizontalScrollIndicator={false}
style={{ flexGrow: 0 }}
keyExtractor={(item, index) => `${item}-${index}`}
renderItem={({ item: tab }) => {
return (
<TouchableOpacity onPress={() => setSelectedTab(tab)}>
<View style={[styles.pill,
{
backgroundColor: selecedTab === tab ? 'gold' : 'transparent',
},
]}>
<Text style={[styles.pillText, { color: selecedTab === tab ? 'white' : 'black' }]}>{tab}</Text>
</View>
</TouchableOpacity>
)
}}
/>
<FlatList
data={popularFood}
keyExtractor={item => item.key}
renderItem={({ item }) => {
return (
<View style={{ flexDirection: 'row' }}>
<Image source={{ uri: item.image }} style={{ width: 100, height: 100, margin: 10 }} />
<View>
<Text style={{ fontSize: 20, fontWeight: 'bold' }}>{item.type}</Text>
<View>
<AntDesign name="star" size={20} color="gold" style={{ marginRight: 10 }} />
<Text style={{ fontSize: 20, fontWeight: 'bold' }}>{item.rating}</Text>
</View>
</View>
</View>
)
}}
/>
<Text style={styles.title}>Address</Text>
<Text style={styles.title}>{address}</Text>
</LinearGradient>
</View>
);
};
Error:
ERROR Warning: React has detected a change in the order of Hooks called by MainMenuScreen. This will lead to bugs and errors if not fixed. For more information, read the Rules of Hooks: https://reactjs.org/link/rules-of-hooks
It actually happens because of any bad practice for hook implementations
1 - Only Call Hooks at the Top Level
Don’t call Hooks inside loops, conditions, or nested functions. Instead, always use Hooks at the top level of your React function
NOTE: Implement your useState hooks first at top of the function
2 - Only Call Hooks from React Functions
Don’t call Hooks from regular JavaScript functions
3 - Getting Error during Test
If you get This Error when testing the component, be careful to where you set your custom hooks (replace to top of the function)
Best Practice
use eslint for lint your code avoid geting React Hooks Rules errors
install package with npm or yarn
npm install eslint-plugin-react-hooks --save-dev

How to pass different event handlers for the same event?

I have a react-native component which is rendering 2 Icons for marking a favorite and writing a comment as shown below:
function RenderDish(props) {
const dish = props.dish;
if (dish != null) {
return (
<Card featuredTitle={dish.name} image={{ uri: baseUrl + dish.image }}>
<Text style={{ margin: 10 }}>
{dish.description}
</Text>
<View style={{ display: "flex", flexDirection: "row", alignItems: "center", justifyContent: "center" }}>
<Icon raised reverse name={props.favorite ? 'heart' : 'heart-o'} type='font-awesome' color='#f50'
onPress={() => props.favorite ? console.log('Aleady Favorite') : props.onFavorite()} />
<Icon raised reverse name='pencil' type='font-awesome' color='#3b5998'
onPress={() => props.onComment()} />
</View>
</Card>
);
}
else {
return (<View></View>);
}}
I am calling this functional component from the outer component as shown below:
<RenderDish dish={this.props.dishes.dishes[+dishId]}
favorite={this.props.favorites.some(el => el === dishId)}
onFavorite={() => this.markFavorite(dishId)}
onComment={() => this.toggleModal()} />
I have already implemented the toggleModal() and the markFavorite() methods and everything is working as expected but my question is: Is there any other way of passing 2 or more different event handlers through a single prop ? For eg. Is there any way to say something like: <RenderDish dish={xyz} onPress={()=> handler1 AND handler2}. Or is there any elegant alternative to what I have done(if I had 5 buttons I would need 5 props :( ) ?
You can have your handlers in a single function and call the function in onPress.
or just
onPress={()=> {handler1, handler2}}
Try something like below. Pass the handler methods as an object in a props.
function Button(props) {
return (
<div>
<button type="button" onClick={props.onPress.handler1} >Handler1</button>
<button type="button" onClick={props.onPress.handler2} >Handler2</button>
</div>
);
}
class Home extends Component {
handler1() {
console.log('handler 1');
}
handler2() {
console.log('handler 2');
}
render() {
return (
<div>
<Button onPress={{ handler1: this.handler1, handler2: this.handler2 }} />
</div>
)
}
}
The way that you’ve done it is probably the most common way and perfectly acceptable.
It’s not generally a problem if there are only a few handlers (and they aren’t being passed deeply).
You could bundle them into an object and single prop, but I don’t typically see it done that way and I don’t think there’s a strong benefit.
You can also pass in multiple props as a single spread object without having to bundle them into a single prop.
If you start getting a more handlers than you’re comfortable with, then that would probably be a good time to look at how you’re handling your state management. You may be better off with a reducer and dispatching actions at that point.
Update
A few other notes about your code:
You can destructure props for easier use.
You do not need a separate closing tag for View if there is no content.
You do not have to make new arrow functions for event handlers if all you are doing is calling another arrow function. You can just set it to the first arrow function.
Consider a ternary to allow an implicit return.
function RenderDish({dish, favorite, onFavorite, onComment}) =>
dish
? <Card featuredTitle={dish.name} image={{ uri: baseUrl + dish.image }}>
<Text style={{ margin: 10 }}>
{dish.description}
</Text>
<View style={{ display: "flex", flexDirection: "row", alignItems: "center", justifyContent: "center" }}>
<Icon raised reverse name={favorite ? 'heart' : 'heart-o'} type='font-awesome' color='#f50'
onPress={() => favorite ? console.log('Aleady Favorite') : onFavorite()} />
<Icon raised reverse name='pencil' type='font-awesome' color='#3b5998'
onPress={onComment} />
</View>
</Card>
: <View/>
I would focus more on these items, rather than bundling those two functions.

React-Native How to change state value for each item?

I have a 'touchableOpacity' where each time its pressed it shows the selected value, but when a value is selected every item gets that value.
I need to do it this way:
item 1 -> selectedValue3
item 2 -> selectedValue1...
{ this.state.proyectosConTodo.map((item,index)=>{ return (
<View style={{paddingBottom:12}}>
<Grid>
<Col>
<View style={{height:40,justifyContent: 'center'}}>
<Text numberOfLines={1} allowFontScaling={false} style={{color: '#45526e',fontFamily: 'Roboto-Regular',fontSize:15, alignSelf: 'flex-start'}}>{item.proyecto.titulo}</Text>
</View>
</Col>
<Col>
<TouchableOpacity style={{ height:40,borderWidth:1, borderRadius:4, borderColor: '#e0e4eb', justifyContent: 'center',backgroundColor: '#f3f4f6'}} onPress={()=>{ this.setAgentesReasignarEnviar(item,index) }}>
<Text allowFontScaling={false} style={{color: '#45526e',fontFamily: 'Roboto-Medium',fontSize:15, marginLeft:10,marginRight:10}}>{this.state.txt_agenteProyecto}</Text>
<Icon style={{position: 'absolute',top:13,right:10}} name={ 'chevron-down'} size={10} color={ '#45526e'}/>
</TouchableOpacity>
</Col>
</Grid>
</View>
) }) }
From your snippet it seems that your structure is that one screen with a FlatList or something similar that renders multiples of your TouchableOpacity components. The problem is that every time a single TouchableOpacity is clicked it changes the state of the screen(not just itself) causing all touchable opacities to have that value.
One possible solution is the create another component which renders your list item and has its own state. The item used to render it can be passed as a prop and then calling setState inside that component will not affect the other list items.
I think this is what you need:
setAgentesReasignarEnviar(item, index) {
const result = this.state.proyectosConTodo.map(() => return item);
this.setState({
proyectosConTodo: result
});
}
For more detail, check this out: How to replace all undefined values in an array with "-" ?

React Native FlatList keyboardShouldPersistTaps not persisting

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

How to render Container Classes conditionally

firstly, this is what is given to me from designer http://www.giphy.com/gifs/hSRrqF5ObsbXH27V09
Basically, there is a category which is passed from previous screen. and with some ui interactions, i need to render this screen again and again. the flow is like that: you select a category, if it has subCategories, let user select one of those subCategories before rendering input components. i can make it work with if and else clauses but i feel that this is some how not best practice at all. I just need an advice from experieced developer(i am reletively new to react native.)
So before writing any code with native way, i just want to ask it here so maybe i can learn more about it.
Here is my Screen:
<NewAdHoc
contentText={'Kategori Secimi'}
onBackPress={this.handleBackPress}
showContentText={false}
>
<View style={styles.container}>
{currentCategory !== null
? (
<View style={{ ...styles.flatListContainer, paddingLeft: undefined, paddingRight: undefined }}>
<View style={{ marginHorizontal: 20 }}>
<ListViewItem
categoryName={currentCategory.categoryName}
iconName={currentCategory.categoryIcon}
showBorder={false}
key={currentCategory.categoryId}
categoryId={currentCategory.categoryId}
inNewAdScreen={false}
/>
</View>
<Seperator
backgroundColor={colors.SEPERATOR_BCK}
text={'Ilan Turu'}
style={{ paddingHorizontal: 20 }}
/>
{
currentCategory.subCategories.map((subc) => (
<View style={{ marginHorizontal: 20 }}>
<SubCategoryItem
text={subc.subCategoryName}
key={subc.subCategoryId}
showBorder={true}
/>
</View>
))
}
</View>
) : null}
</View>
</NewAdHoc>
right now, i am rendering a category, a <Seperator/> between category and subcategories, and subcategories. what i want is that, when user click on one of the subCategories, i will change the state to isSubCategorySelected = true, selectedSubCategory= subCategoryId and then need to render the whole screen like in gif i provided above.
the For those who came here for answer:
What i did is basically divide and conquer paradigm. I firstly divide my sitution into two main state.
RenderInitialForm(),
renderAfterSubCategorySelected(),
those two rendering functions handle the whole process. when a TouchableOpacity is clicked, i setState with two variables: isSubCategorySelected = true, selectedSubCategory = subCategory
and in my main render() function:
render() {
const { currentCategory, isSubCategorySelected, selectedSubCategory } = this.state
return (
<NewAdHoc
contentText={'Kategori Secimi'}
onBackPress={this.handleBackPress}
showContentText={false}
>
<View style={styles.container}>
{currentCategory !== null
? isSubCategorySelected
? this.renderAfterSubCategorySelected()
: this.renderInitialForm()
: null}
</View>
</NewAdHoc>
);
}
if you have any suggestion with my solution, please feel free to contact me.

Categories

Resources