React native card (native base) onPress doesn't work - javascript

I want to display cards with news from a JSON file.
Getting the JSON works fine, but I want to add a onPress event, so I can click the card and navigate to the article.
My card view:
<Card>
<CardItem button onPress={this._OnButtonPress(news.id)}>
<Left>
<Body>
<Text style={styles.cardText}>{news.title}</Text>
</Body>
</Left>
</CardItem>
<CardItem>
<Left>
<Icon style={styles.icon} name="chatbubbles" />
<Text>{news.comments} comments</Text>
</Left>
<Right>
<Text>{news.published}</Text>
</Right>
</CardItem>
</Card>
I am trying to pass a variable to the onButtonPress() function
_OnButtonPress(newsID) {
Alert.alert(newsID.toString());
}
To test the onPress event, all I did is alert the parameter.
I don't see anything wrong, but still I got this error
Does anyone know how I can fix this and what Im doing wrong here.
Thanks in advance.
Update
My updated class:
import React, { Component } from "react";
import {
Image,
ListView,
StyleSheet,
Text,
View,
Alert
} from 'react-native';
import {
Container,
Header,
Left,
Right,
Button,
Card,
CardItem,
Icon,
Body,
Content,
Logo
} from 'native-base';
import styles from "./styles";
const logo = require("../../../img/f1today.png");
var REQUEST_URL = 'http://v2.first-place.nl/test/news.json';
class News extends Component {
constructor(props) {
super(props);
this._OnButtonPress = this._OnButtonPress.bind(this);
this.state = {
dataSource: new ListView.DataSource({
rowHasChanged: (row1, row2) => row1 !== row2,
}),
loaded: false,
};
}
_OnButtonPress(newsID) {
Alert.alert(newsID.toString());
}
componentDidMount() {
this.fetchData();
}
fetchData() {
fetch(REQUEST_URL)
.then((response) => response.json())
.then((responseData) => {
this.setState({
dataSource: this.state.dataSource.cloneWithRows(responseData.articles),
loaded: true,
});
})
.done();
}
render() {
if (!this.state.loaded) {
return this.renderLoadingView();
}
return (
<Container style={styles.container}>
<Header
style={{ backgroundColor: "#fff" }}
androidStatusBarColor="#f05423"
iosBarStyle="light-content">
<Left>
<Button
transparent
onPress={() => this.props.navigation.navigate("DrawerOpen")}
>
<Icon name="ios-menu" style={{color: 'black'}} />
</Button>
</Left>
<Body>
<Image source={logo} style={styles.headerLogo} />
</Body>
<Right />
</Header>
<Content padder>
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderNews}
style={styles.listView}
/>
</Content>
</Container>
);
}
renderLoadingView() {
return (
<View style={styles.loading}>
<Text>
Loading news...
</Text>
</View>
);
}
renderNews(news) {
return (
<Card>
<CardItem button onPress={()=> this._OnButtonPress(news.id)}>
<Left>
<Body>
<Text style={styles.cardText}>{news.title}</Text>
</Body>
</Left>
</CardItem>
<CardItem>
<Left>
<Icon style={styles.icon} name="chatbubbles" />
<Text>{news.comments} comments</Text>
</Left>
<Right>
<Text>{news.published}</Text>
</Right>
</CardItem>
</Card>
);
}
}
export default News;

You should wrap your CardItem with TouchableOpacity or any other Touchable item. And give onPress function to that Touchable item.
<TouchableOpacity onPress={() => //your function}
<CardItem>
</CardItem>
</TouchableOpacity>
Note: Make sure you use the <TouchableOpacity> component associated with the react-native package rather than the react-native-gesture-handler package. Visual Studio Code editor may auto-import from react-native-gesture-handler which does not work in this particular use case.
Correct Package:
import { TouchableOpacity } from 'react-native';
Incorrect Package:
import { TouchableOpacity } from 'react-native-gesture-handler';

For me i left the button={true} prop off, when i added it works. See example below.
<CardItem
button={true}
onPress={() => {this.cardSelected(item.name)}}
style={{paddingTop:0,paddingBottom:0,paddingLeft:0,paddingRight:0}}>
<Image source={item.img} style={{flex:1,resizeMode: 'cover',height: 175}} />
<Text style={styles.cardheading}>{item.name}</Text>
<Image source={cardline} style={styles.cardline}/>
<Text style={styles.cardtext}> {item.text}</Text>
</CardItem>

The problem you are having is that the method cannot access the scope of the view. Right now you have your renderNews method defined this way:
renderNews(news) { }
If you declare your method this way you will not have this available on your method and as "this" is undefined, all the methods will trigger an error because you are trying to access to "undefined.methodName()". Having said that, you should "tie" the context to your method declaring it this way:
renderNews = (news) => { }
Now you have the context attached to the method and "this" is accesible inside.

You need to bind this function. Write the following code inside your constructor function:
this._OnButtonPress = this._OnButtonPress.bind(this);
Also, changed the onPress as following:
onPress={()=> this._OnButtonPress(news.title)}
Finally, i could see the onPress been written on the component. Rather you should define/write it inside that container.

Related

typeError: Cannt read property 'goBack' of undefined

So the scenario is, I want to implement a GoBack Icon at the top of my app,that leads back to previous page. I am using stack navigator and disabled the header. So I need a go back button. I decided to make a component for that, here is my code,
import { Ionicons } from '#expo/vector-icons';
function GoBack(){
return (
<Ionicons onPress={()=>this.props.navigation.goBack()} name="md-arrow-back" size={24} color="#0c85f3" />
);
}
export default GoBack;
if I do it like this then an it shows me a typeError: Cannt read property 'goBack' of undefined.but if I put onPress as props and implement the same line of code onPress={()=>this.props.navigation.goBack()} it works perfectly.
I can not apply onPress props everywhere. Its a app with a lot of screens. How do I apply it in the component itself?
I think I am in lack of deep understanding of React navigation. Please help me understand the solution too.
Here is how I am using the GoBack Component:
import React, { Component } from 'react';
import {View, StyleSheet, Text, FlatList} from 'react-native';
import TestRoomtData from '../../../testData';
import HistoryCard from '../../../components/cards/historyUserCard';
import GoBack from '../../../shared/goBackButton';
class UserHistory extends Component{
render(){
return(
<View style={styles.container}>
<View style={{flexDirection:"row",}}>
<GoBack />
<Text style={styles.title}>History</Text>
</View>
<Text style={styles.title01}>Past Patients</Text>
<FlatList
data={TestRoomtData}
renderItem={({item})=>([
<View>
<HistoryCard
prescription={item.prescription}
diagnosis={item.diagnosis}
date={item.date}
source={{
uri: item.uri
}}
btnText01="More"
btnText02="Edit"
onPressBtn2={()=> this.props.navigation.navigate('Edit History') }/>
</View>
]
)}
/>
</View>
);
}
}
It's a functional component,
First : so you can't use this. ,
Second : you forgot to get props
function GoBack(props){ // <---- HERE
return (
<Ionicons
onPress={props.navigation.goBack} // <---- HERE
name="md-arrow-back" size={24} color="#0c85f3" />
);
}
In functional component in react, you can't use this keyword to access props.
In the current situation you have to pass navigation prop in GoBack
something like this,
function Goback(props) {
const { navigation } = props;
return (
<Ionicons
onPress={() => navigation.goBack()}
name="md-arrow-back"
size={24}
color="#0c85f3"
/>
);
}
Now, call the GoBack component,
<GoBack
navigation={{ goBack: () => goBack() }}
/>

Delete Hook, deletes all the entries in the database automatically

I created a delete hook to individually delete some data on a screen but every-time I load the hook, it deletes all the entries on the page. I am trying to be able to click an icon and delete an item. I am passing the id of the item through the props, then calling my delete function but instead of waiting for user input the hook deletes everything in the database. Any help is appreciated.
Hook
import { useContext } from 'react';
import { Context as CertificationContext } from '../context/CertificationContext';
import { Context as CertificateInfoContext } from '../context/CertificateInfoContext';
export default () => {
const { deleteCertification } = useContext(CertificationContext);
const {
state: { certificationId }
} = useContext(CertificateInfoContext);
console.log('Delete' + certificationId);
const removeCertification = async id => {
await deleteCertification(id);
};
return removeCertification;
};
Component
import { View, Text, StyleSheet } from 'react-native';
import { Button } from 'react-native-elements';
import Icon from 'react-native-vector-icons/Feather';
import { navigate } from '../navigationRef';
import useDeleteCertification from '../hooks/useDeleteCertification';
const Certifications = ({ title, month, description, id }) => {
const removeCertification = useDeleteCertification();
return (
<View style={styles.mainContainer}>
<View style={styles.firstContainer}>
<Text style={styles.monthText}>{month}</Text>
<Text style={styles.dayText}>FRI</Text>
</View>
<View style={styles.secondContainer}>
<Text style={styles.title}>{title}</Text>
<Text style={styles.subtitle}>{description}</Text>
<View style={styles.buttonView}>
<Button
icon={<Icon name='trash-2' color='grey' size={15} />}
type='clear'
onPress={removeCertification(id)}
/>
<Button
icon={<Icon name='share' color='grey' size={15} />}
type='clear'
onPress={() => {
console.log('Share Button');
}}
/>
<Button
icon={<Icon name='edit' color='grey' size={15} />}
type='clear'
onPress={() => {
navigate('EditCertification', { id });
}}
/>
</View>
</View>
</View>
);
};
Use an arrow function in your onPress prop like this:
<Button
icon={<Icon name='trash-2' color='grey' size={15} />}
type='clear'
onPress={() => removeCertification(id)}
/>
Now you are passing a function rather than the result of a function, so it won't trigger on render

How to pass and execute functions as props in class Component in React Native?

I'm a beginner in React Native and struggling in passing and executing functions as props from parent to child component. Here's the code:
MainMap
import React from 'react';
import {
TouchableWithoutFeedback,
StyleSheet,
View,
Button,
FlatList,
Dimensions
} from 'react-native';
import PlaceInput from '../components/PlaceInput';
const INCREMENT = 1;
const HEIGHT = Dimensions.get('window').height
const WIDTH = Dimensions.get('window').width
class MainMap extends React.Component{
constructor(props){
super(props);
this.state={
numOfInput:[],
counter: 0,
}
this.onAddSearch = this.onAddSearch.bind(this)
this.onDeleteSearch = this.onDeleteSearch.bind(this)
}
onAddSearch(){
this.setState((state) => ({
counter: state.counter + INCREMENT,
numOfInput: [...state.numOfInput, state.counter]
}))
}
onDeleteSearch(inputId){
const items = this.state.numOfInput.filter(item => item.id !== inputId)
this.setState({
numOfInput: items
})
}
render(){
return(
<TouchableWithoutFeedback onPress={this.hideKeyboard} >
<View style={styles.container} >
<Button title='Add a location' onPress={this.onAddSearch} />
<View style={{height: HEIGHT/2 }}>
<FlatList
data={this.state.numOfInput}
keyExtractor={(item, index) => item.id}
renderItem={itemData => {
return(
<PlaceInput
key={itemData.item.id}
// id={itemData.item.id}
onDelete={this.onDeleteSearch}
showDirectionOnMap={this.showDirectionOnMap}
userLatitude={userLatitude}
userLongitude={userLongitude}
/>
)
}}
/>
</View>
</View>
</TouchableWithoutFeedback>
)
}
}
export default MainMap;
const styles = StyleSheet.create({
container:{
flex: 1
},
})
Here's the PlaceInput component
class PlaceInput extends React.Component{
constructor(props){
super(props);
... // These lines have no relation to what I'm asking so don't mind them
}
...
render(){
return(
<View style={styles.buttonContainer} >
<View style={{flex: 1, alignItems: 'center'}}>
<Text style={{fontSize: 8}}>{'\u25A0'}</Text>
</View>
<View style={{flex: 4}}>
<TextInput
autoCorrect={false}
autoCapitalize='none'
style={styles.inputStyle}
placeholder='Search your places'
onChangeText={(input) => {
this.setState({destinationInput: input});
this.getPlacesDebounced(input);
}}
value={this.state.destinationInput}
/>
{/* {predictions} */}
</View>
<View style={styles.rightCol}>
<TouchableOpacity onPress={this.props.onDelete.bind(this, this.props.id)}>
<Ionicons name='md-car' size={25} style={{alignSelf: 'center'}} />
</TouchableOpacity>
</View>
</View>
)
}
}
What I'm trying to do:
Define a function to execute in MainMap.js (in FlatList --> PlaceInput for specific) , which is to delete an search bar( the whole PlaceInput in the FlatList) every time I click the right symbol of that search bar. The function is onDeleteSearch
The right symbol is styled in a TouachableOpacity as you can see in the PlaceInput.js component. I put it in the last View pair
However, When I click, the screen deletes all the search bars, not the one I click. Is it the problem of the id of the component PlaceInput ? Or with the way I call the props?...
Please help me !
<TouchableOpacity onPress={this.props.onDelete.bind(this, this.props.id)}>
<Ionicons name='md-car' size={25} style={{alignSelf: 'center'}} />
</TouchableOpacity>
Don't bind, just call this.props.onDelete(this.props.id);
In MainMap, try this:
<PlaceInput
key={itemData.item.id}
// id={itemData.item.id}
onDelete={() => this.onDeleteSearch(itemData.item.id)} // here
showDirectionOnMap={this.showDirectionOnMap}
userLatitude={userLatitude}
userLongitude={userLongitude}
/>
Assuming the function:
onPressed(optionalArgument = false) {
// do something
}
You can pass a function to onPress if it does not require any arguments, i.e
onPress={onPressed} // - would work if no arguments required.
onPress={onPressed(argument)} // - will get fired on component render
onPress={()=> onPressed(argument)} // - will work as expected on button press
onPress={()=> { // - will work as expected on button press
// Multiple lines of code
onPressed(argument);
anotherFunction();
}
};
In your MainMap you are doing everything correctly, just uncomment the
// id={itemdata.item.id}
In PlaceInput, just one small change:
<TouchableOpacity onPress={() => this.props.onDelete(this.props.id)}>
<Ionicons name='md-car' size={25} style={{alignSelf: 'center'}} />
</TouchableOpacity>
If you don't add ()=> to your onPress, the function gets called immediately, that's why you see such behaviour.
Your numOfInput is just a list of numbers, so instead of using item.id-s use item directly.
Here:
const items = this.state.numOfInput.filter(item => item !== inputId)
And here
<PlaceInput
key={itemData.item}
// id={itemData.item}
...
/>

React-Native another VirtualizedList-backed container

After upgrading to react-native 0.61 i get a lot of warnings like that:
VirtualizedLists should never be nested inside plain ScrollViews with the same orientation - use another VirtualizedList-backed container instead.
What is the other VirtualizedList-backed container that i should use, and why is it now advised not to use like that?
If someone's still looking for a suggestion to the problem that #Ponleu and #David Schilling have described here (regarding content that goes above the FlatList), then this is the approach I took:
<SafeAreaView style={{flex: 1}}>
<FlatList
data={data}
ListHeaderComponent={ContentThatGoesAboveTheFlatList}
ListFooterComponent={ContentThatGoesBelowTheFlatList} />
</SafeAreaView>
You can read more about this here: https://facebook.github.io/react-native/docs/flatlist#listheadercomponent
Hopefully it helps someone. :)
Just in case this helps someone, this is how I fixed the error in my case.
I had a FlatList nested inside a ScrollView:
render() {
return (
<ScrollView>
<Text>{'My Title'}</Text>
<FlatList
data={this.state.myData}
renderItem={({ item }) => {
return <p>{item.name}</p>;
}}
/>
{this.state.loading && <Text>{'Loading...'}</Text>}
</ScrollView>
);
}
and I got rid of the ScrollView by using the FlatList to render everything I needed, which got rid of the warning:
render() {
const getHeader = () => {
return <Text>{'My Title'}</Text>;
};
const getFooter = () => {
if (this.state.loading) {
return null;
}
return <Text>{'Loading...'}</Text>;
};
return (
<FlatList
data={this.state.myData}
renderItem={({ item }) => {
return <p>{item.name}</p>;
}}
ListHeaderComponent={getHeader}
ListFooterComponent={getFooter}
/>
);
}
The best way is to disable that warning because sometimes Flatlist need to be in ScrollView.
UPDATE RN V0.63 ABOVE
YellowBox is now changed and replace with LogBox
FUNCTIONAL
import React, { useEffect } from 'react';
import { LogBox } from 'react-native';
useEffect(() => {
LogBox.ignoreLogs(['VirtualizedLists should never be nested']);
}, [])
CLASS BASED
import React from 'react';
import { LogBox } from 'react-native';
componentDidMount() {
LogBox.ignoreLogs(['VirtualizedLists should never be nested']);
}
UPDATE RN V0.63 BELOW
FUNCTIONAL
import React, { useEffect } from 'react';
import { YellowBox } from 'react-native';
useEffect(() => {
YellowBox.ignoreWarnings(['VirtualizedLists should never be nested']);
}, [])
CLASS BASED
import React from 'react';
import { YellowBox } from 'react-native';
componentDidMount() {
YellowBox.ignoreWarnings(['VirtualizedLists should never be nested']);
}
Data
// dummy data array
const data = [
{id: 1, name: 'Tom'},
{id: 2, name: 'Jerry'},
]
Solution #1
You can make a custom component for that like this
const VirtualizedList = ({children}) => {
return (
<FlatList
data={[]}
keyExtractor={() => "key"}
renderItem={null}
ListHeaderComponent={
<>{children}</>
}
/>
)
}
then use this VirtualizedList as parent component:
...
return (
<VirtualizedList>
<FlatList
data={data}
keyExtractor={(item, index) => item.id + index.toString()}
renderItem={_renderItem}
/>
<AnyComponent/>
</VirtualizedList>
)
Solution #2
If you use FlatList inside the ScrollView it gives warning which is annoying, so you can use array's map property, like this -
NOTE: It is not recommended way to show list. If you have small amount of that then you can use it that's totally fine, but if you want to show a list which get data from api and have lot's of data then you can go with other solutions. if you use map with large data then it affect your app performance
<ScrollView>
{data.map((item, index) => (
<View key={index}>
<Text>{item.name}</Text>
</View>
))}
</ScrollView>
Solution #3
if you make your FlatList horizontal (as per your need) then also warning will disappear
<ScrollView>
<FlatList
data={data}
keyExtractor={(item, index) => item.id + index.toString()}
horizontal={true}
/>
</ScrollView>
Solution #4
you can add header and footer component
In ListHeaderComponent and ListFooterComponent you can add any component so you don't need parent ScrollView
<FlatList
data={data}
keyExtractor={(item, index) => item.id + index.toString()}
ListHeaderComponent={headerComponent}
ListFooterComponent={footerComponent}
ListEmptyComponent={emptyComponent}
ItemSeparatorComponent={separator}
/>
// List components
const headerComponent = () => (
<View>
<Header/>
<Any/>
</View>
)
const footerComponent = () => (
<View>
<Footer/>
<Any/>
</View>
)
const emptyComponent = () => (
<View>
<EmptyView/>
<Any/>
</View>
)
const separator = () => (
<View style={{height: 0.8, width: '100%', backgroundColor: '#fff'}} />
)
The warning appears because ScrollView and FlatList share the same logic, if FlatList run inside ScrollView, it's duplicated
By the way SafeAreaView doesn't work for me, the only way to solve is
<ScrollView>
{data.map((item, index) => {
...your code
}}
</ScrollView>
The error disappears
Looking at the examples in docs I've changed container from:
<ScrollView>
<FlatList ... />
</ScrollView>
to:
<SafeAreaView style={{flex: 1}}>
<FlatList ... />
</SafeAreaView>
and all those warnings disappeared.
In my case, I needed to have FlatLists nested in a ScrollView because I am using react-native-draggable-flatlist to move ingredients and steps around in a recipe.
If we read the warning properly, it says that we should use another VirtualizedList-backed container to nest our child FlatList in. What I did is:
/* outside the component */
const emptyArry = []
/* render */
<FlatList
scrollEnabled={false}
horizontal
data={emptyArray}
ListEmptyComponent=(<DraggableList />)
/>
No more warning, and I think this is the pattern recommended by the warning.
<ScrollView horizontal={false} style={{width: '100%', height: '100%'}}>
<ScrollView horizontal={true} style={{width: '100%', height: '100%'}}>
<FlatList ... />
</ScrollView>
</ScrollView>
Below code works perfectly for me to disable annoying error:
VirtualizedLists should never be nested inside plain ScrollViews with the same orientation because it can break windowing and other functionality - use another VirtualizedList-backed container instead.
React Native 0.68.2
<ScrollView horizontal={false} style={{flex: 1}}>
<ScrollView
horizontal={true}
contentContainerStyle={{width: '100%', height: '100%'}}>
<FlatList ... />
</ScrollView>
</ScrollView>
I tried some ways to solve this, including ListHeaderComponent or ListFooterComponent, but it all didn't fit for me.
layout I wanted to achieve is like this, and I wanted to get scrolled in once.
<ScrollView>
<View>I'm the first view</View>
<View>I'm the second view</View>
<MyFlatList />
</ScrollView>
First I want to say thanks to this issue and comments, which gave me bunch of ideas.
I was thinking of ListHeaderComponent places above the Flatlist, but since my Flatlist's direction was column, the header I wanted to place went on the left of the Flatlist :(
Then I had to try on VirtualizedList-backed thing. I just tried to pack all components in VirtualizedList, where renderItems gives index and there I could pass components conditionally to renderItems.
I could have worked this with Flatlist, but I haven't tried yet.
Finally it looks like this.
<View>
<Virtualizedlist
data={[]}
initialNumToRender={1}
renderItem={(props)=>{
props.index===0 ? (1st view here) : props.index===1 ? (2nd view here) : (my flatlist)
}}
keyExtractor={item => item.key}
getItemCount={2}
getItem={ (data, index) => {
return {
id: Math.random().toString(12).substring(0),
}
}}
/>
</View>
(inside which lazyly renders↓)
<View>I'm the first view</View>
<View>I'm the second view</View>
<MyFlatList />
and of course able to scroll the whole screen.
As #Brahim stated above, setting the horizontal property to true seem to resolve the issues for a FlatList embedded in a ScrollView.
So I faced the same problem while using a picker-based component inside <ScrollView> and the one thing that helped me solve the problem was adding
keyboardShouldPersistTaps={true} inside the <ScrollView> as a prop.
This is my code snippet.
<ScrollView keyboardShouldPersistTaps={true}>
<SelectionDD studentstatus={studentStatus}/>
<SearchableDD collegeNames={collegeNames} placeholder='University'/>
</ScrollView>
I have two Flatlist; each of them has many Item also has a feature to collapse and expand.
Because of that, I can't use SafeAreaView.
I saw another solution and found a new way.
I define one Flatlist in the core component ( without Scrollview) and render each Flatlist with a map function inside ListHeaderComponent and ListFooterComponent.
<View style={{flex: 1}}>
<FlatList
style={{backgroundColor: 'white'}}
refreshing={loading}
onRefresh={() => sample()}
ListHeaderComponent = {
<View>
{collapse/expandComponent}
{this.state.sample1&& content1.map((item, index) => this.renderList1(item,index))}
</View>
}
ListFooterComponent = {
<View>
{collapse/expandComponent}
{this.state.sample2 && content2.map((item, index) => this.renderlist2(item,index))}
</View>
}
/>
</View>
In my opinion i can use map instead of FlatList. But in my case i wan't to show large list. Not using FlatList may cause performance issue. so i used this to suppress warning https://github.com/GeekyAnts/NativeBase/issues/2947#issuecomment-549210509
Without hiding YellowBox you still can implement scroollable view inside scrollable view. You can use this library. It replace the default Scrollview from React Native.
This may help someone down the line, be sure you to check how your components are nested. Removing the ScrollView from the top component fixed the issue.
I ran into this issue because I had two components nested like this essentially:
Component 1
<ScrollView>
<OtherStuff />
<ListComponent />
</ScrollView>
My second component 'ListComponent' had a FlatList already wrapped with SafeAreaView.
<SafeAreaView style={styles.container}>
<FlatList
data={todoData}
renderItem={renderItem}
ItemSeparatorComponent={() => <View style={styles.separator} />}
keyExtractor={item => item.id.toString()}
/>
</SafeAreaView>
In the end I replaced the ScrollView from the first component with a View instead.
Use flatList like this ListHeaderComponent and ListFooterComponent:
<FlatList ListHeaderComponent={
<ScrollView
style={styles.yourstyle}
showsVerticalScrollIndicator={false}
>
<View style={styles.yourstyle}>
</View>
</ScrollView>
}
data={this.state.images}
renderItem={({ item, index }) => {
return (
<View
style={styles.yourstyle}
>
<Image
source={{
uri: item,
}}
style={styles.yourstyle}
resizeMode={"contain"}
/>
<Text
numberOfLines={2}
ellipsizeMode="tail"
style={styles.yourstyle}
>
{item.name}
</Text>
</View>
);
}}
keyExtractor={({ name }, index) => index.toString()}
ListFooterComponent={
<View style={styles.yourstyle}></View>
}
/>
If you use ScrollView and FlatList together you'll get inconsistent scroll behaviour.
So just remove ScrollView and use FlatList in a View.
<View flex={1}>
<FlatList
data={list}
renderItem={({ item }) => this.renderItem(item) }
keyExtractor={item => item.id}
/>
</View>
import React from 'react';
import { FlatList, ScrollViewProps } from 'react-native';
export const ScrollView: React.FC<ScrollViewProps> = props => {
return (
<FlatList
{...props}
data={[]}
renderItem={() => null}
ListHeaderComponent={() => (
<React.Fragment>{props.children}</React.Fragment>
)}
ListEmptyComponent={null}
keyExtractor={() => 'blank'}
/>
);
};
This will essentially work exactly like a ScrollView except without this error.
I was having this issue using a scrollview as parent view, and nesting a SelectBox from react-native-multi-selectbox package. I was able to solve this by adding listOptionProps={{nestedScrollEnabled: true}} like this:
<ScrollView>
<SelectBox
label="Select single"
options={serverData}
listOptionProps={{nestedScrollEnabled: true}}
value={input.elementSelected}
onChange={event =>
inputHandlerLang('elementSelected', event, key)
}
hideInputFilter={false}
/>
</ScrollView>
the error still present but scrolling within SelectBox works as well as within the parent scrollview. I also do have to suppress the error with LogBox. I don't know if there are any drawbacks to this but I'll try to test this more.
Update 1: this used to work in v0.68.2, but since I updated to patch v0.68.5, the warning became an error.
You have to remove ScrollView and enable scroll from FlatList using the property scrollEnabled={true}, you can place the other views inside ListHeaderComponent and ListFooterComponent
<View flex={1}>
<FlatList
data={data}
scrollEnabled={true}
showsVerticalScrollIndicator={false}
renderItem={({ item }) => (
<Text>{item.label}</Text>
)}
keyExtractor={item => item.id}
ListHeaderComponent={() => (
<Text>Title</Text>
)}
ListFooterComponent={() => (
<Text>Footer</Text>
)}
/>
</View>
Actually as I know, using nested VirtualizedLists, caused always performance issues, just the warning to that issue is new. I tried everything I found on the internet, non of them helped. I use now ScrollView or when you just have a normall View with maxHeight then you will be able to scroll if the content-height is bigger then the maxHeight of you View.
So:
<ScrollView>
{items.map((item, index) =>
<YourComponent key={index} item={item} />
)}
</ScrollView>
Or just:
<View style={{maxHeight: MAX_HEIGHT}}>
{items.map((item, index) =>
<YourComponent key={index} item={item} />
)}
</View>
This error disappeared because of using FlatList inside ScrollView. You can write like the following code.
<SafeAreaView style={styles.container}>
<View style={styles.container}>
<View>
<Header />
</View>
{(list.length == 0) &&
<View style={{flex:1, margin: 15}}>
<Text style={{textAlign: 'center'}}>No peripherals</Text>
</View>
}
<FlatList
data={list}
renderItem={({ item }) => this.renderItem(item) }
keyExtractor={item => item.id}
/>
</View>
</SafeAreaView>
You can add horizontal=True and contentContainerStyle={{ width: '100%' }} to the ScrollView parent.
<ScrollView
style={styles.collaborators}
contentContainerStyle={{ width: '100%' }} <--
horizontal <--
>
<FlatList
data={list?.slice(0, 10) || []}
keyExtractor={item => item.cc}
ItemSeparatorComponent={Separator}
renderItem={({ item }) => (
<Collaborator name={item.name} cc={item.cc} />
)}
/>
</ScrollView>
This worked for me (as a bit of a hack). Use a FlatList with empty data and null renderItem props instead of using a ScrollView
const emptyData = [];
const renderNullItem = () => null;
const App = () => {
const ListFooterComponent = (
<FlatList ... />
);
return (
<SafeAreaView>
<FlatList
data={emptyData}
renderItem={renderNullItem}
ListFooterComponent={ListFooterComponent}
/>
</SafeAreaView>
);
};
I had the same issue, and just got rid of it by removing the ScrollView around the FlatList. Because by default FlatList provides Scroll Functionality based on the length of content that it renders. 😊

React native create custom modal using render props

I want to create a Modal component and I want to have possibility to inject to it everything what I want. Firstly I decided to use HOC but then I've changed my solution to render props. Everything works fine but I don't really like this solution. I'm wondering how could I optimize it to make it better. What is the best practise to create such kind of modal where you have button opening this modal beyond modal component. I really don't like that now I have two components with open/close state of modal. And both of them have a toggle method to open/close modal. Any suggestion? Maybe I should stick with the HOC ?
Here's the code with Component.js where CustomModal is used:
toggleModalVisibility = (visible) => {
this.setState({modalVisible: visible});
};
render() {
const question = this.props.questions[this.props.counter];
return (
<View style={styles.questionContainer}>
<CustomModal
visible={this.state.modalVisible}
toggleModalVisibility={this.toggleModalVisibility}
render={() => (
<>
<Text>{question.text}</Text>
<Text>{question.details}</Text>
</>
)}
/>
<View style={styles.questionTextContainer}>
<Text style={styles.questionText}>{question.text}</Text>
<TouchableOpacity onPress={() => this.toggleModalVisibility(!this.state.modalVisible) }>
<FontAwesome5 name='question-circle' size={30} color='#B7DBF3' light />
</TouchableOpacity>
</View>
</View>
)
}
and here's the code of CustomModal.js
export default class CustomModal extends Component {
constructor(props) {
super(props);
this.state = {
isOpen: this.props.visible
};
}
componentDidUpdate(prevProps) {
if (prevProps.visible !== this.props.visible) {
this.setState({isOpen: this.props.visible});
}
}
toggle = (isOpen) => {
this.setState({ isOpen });
this.props.toggleModalVisibility(isOpen)
}
render() {
return (
<View>
<Modal
animationType='slide'
transparent={false}
visible={this.state.isOpen}
>
<View style={{marginTop: 30, marginLeft: 5}}>
<TouchableHighlight
onPress={() => {
this.toggle(!this.state.isOpen)
}}>
<FontAwesome5 name='times-circle' size={30} light />
</TouchableHighlight>
<View>{this.props.render()}</View>
</View>
</Modal>
</View>
)
}
}

Categories

Resources