React Native TouchableOpacity onPress not working with FlatList - react-native

I created a FlatList of selectable countries like this
export default function Body() {
const data = useData();
const [selected, setSelected] = useState(1);
const renderItem = ({ item }) => {
return item.empty ? (
<View style={styles.blank} />
) : (
<Country
text={item.name}
onPress={() => setSelected(item.id)}
selected={item.id === selected}
>
<item.Flag />
</Country>
);
};
return (
<View style={styles.container}>
<FlatList
data={data}
renderItem={renderItem}
keyExtractor={(item) => item.id.toString()}
extraData={selected}
showsVerticalScrollIndicator={false}
contentContainerStyle={styles.flatlist}
numColumns={3}
/>
</View>
);
}
And I defined a Country component like this
import React from "react";
import { View, Text, TouchableOpacity } from "react-native";
import { styles } from "./Style";
export default function Country({ children, text, onPress, selected }) {
return (
<View style={styles.container}>
<View style={[styles.view, selected && styles.selected]}>
<TouchableOpacity onPress={onPress}>{children}</TouchableOpacity>
</View>
<TouchableOpacity onPress={onPress}>
<Text style={styles.text}>{text}</Text>
</TouchableOpacity>
</View>
);
}
Everything was working perfectly fine and I was able to select a country but somehow now the touchableOpacity onPress event seems broken and the elements are no longer pressable. for things to work I need to edit and undo edit on the data array or the Country component. Is this a RN bug causing things to fail?

I found the root problem. Turns out that the problem lays on the stylesheet of the Country component wrapper. I gave it an alignSelf: "baseline" don't know why but removing it solved the hassle

Related

How to only show one items details from FlatList?

I have a database of toys (Database.js), each toy has 8 details (name, image, id, type, seller, short description, and long description). I have a list of toy cards showing on ViewToys.js page, each card shows 5 of those details. Currently, I have it so that when you click on any of these cards, it goes to the SlugProduct.js page, but its currently showing ALL the toys in the database instead of just the selected toys details (I want it to show all 8 details).
Is there a way for me to tweak this code so that it only shows the one items details? I have used keyExtractor (item.id) - would it be possible to tell react native that when this toy is clicked, only the details with the corresponding id is loaded and shown? Or some other method perhaps? I feel like I should be able to do this with useState, but don't know how I would go about doing that.
I'm wanting to pass data from one component to another, so I'll post the components:
This is the component where you would click the toy card and have it navigate to another page (a slug that loads the next component):
import { StyleSheet, Text, View, FlatList } from 'react-native'
import React, {useState} from 'react'
import Toy from './Database'
import ToyCard from './ToyCard'
const FLToyCard = ({navigation}) => {
const [selectedToy, setSelectedToy] = useState(null)
const headerComp = () => {
return(
<View style={{alignSelf: 'center'}}>
<Text style={{fontSize: 25, padding: 10}}>All Toys For Sale</Text>
</View>
)
}
const renderMyItem = ({item}) => {
return(
<View style={{flex: 1}}>
<ToyCard
name={item.name}
image={item.image}
price={item.price}
desc={item.desc}
seller={item.seller}
value={selectedToy}
onPress={()=>navigation.navigate('SlugProduct')}
/>
</View>
)
}
return(
<View>
<FlatList
data={Toy}
renderItem={renderMyItem}
keyExtractor={(item)=>item.id}
numColumns={2}
ListHeaderComponent={headerComp}
/>
</View>
)
}
export default FLToyCard
This is the component that would receive the data and format it within itself. Currently the component is being implemented in another component that looks like the above one, hence why its showing all the items in the flatlist.
import { StyleSheet, Text, View, ScrollView, Image, Button, TouchableOpacity } from 'react-native'
import React, { useState, useEffect, useRoute } from 'react'
import { AntDesign } from '#expo/vector-icons'
const SlugFormat = ({name, seller, image, id, type, longDesc, price}) => {
const [quantity, setQuantity] = useState(1);
const addQuantity = () => {
setQuantity(quantity + 1);
};
const minusQuantity = () => {
if (quantity !== 1){
setQuantity(quantity - 1);
}
};
return (
<ScrollView style={{backgroundColor: '#ffce20', height: '100%'}}>
<Image style={styles.toyImage} source={image} />
<View>
<View style={{flexDirection: 'row', justifyContent: 'space-between'}}>
<Text style={styles.title}>{name} (#{id})</Text>
<Text style={styles.price}>${price}</Text>
</View>
<View styles={{flexDirection: 'column'}}>
<Text style={styles.links}>Seller: {seller}</Text>
<Text style={styles.links}>Type: {type}</Text>
</View>
<Text style={styles.descriptionHeader}>Description</Text>
<Text style={styles.description}>{longDesc}</Text>
</View>
<View>
<View>
<Text>Quantity: </Text>
<View style={styles.quantityFrame}>
<TouchableOpacity onPress={minusQuantity}>
<AntDesign name="minuscircleo" size={35} color="black" />
</TouchableOpacity>
<Text value={quantity} onChangeText={setQuantity} style={styles.quantity}>{quantity}</Text>
<TouchableOpacity onPress={addQuantity}>
<AntDesign name="pluscircleo" size={35} color="black" />
</TouchableOpacity>
</View>
</View>
<Button title='Add To Cart'/>
</View>
</ScrollView>
)
}
export default SlugFormat
I'll try to explain with what I think is a similar example.
To begin with, we would have our component to show each of the cards of the toys
export const ToyCard = ({ name, desc }) => {
return (
<View>
<Text>{name}</Text>
<Text>{desc}</Text>
</View>
)
}
Then, we would have the page to show the details of each toy, for when the user clicks on one of the FlatList items.
export const ToyDetailsPage = ({ name, desc }) => {
return (
<View>
<Text>{name}</Text>
<Text>{desc}</Text>
</View>
)
}
And finally, we have the page where we are showing the list of toys, and from where we are going to pass the parameters to the page that is going to show the details of the toy
const toys = [
{ id: '1', name: 'toy1', desc: 'desc toy1' },
{ id: '2', name: 'toy2', desc: 'desc toy2' },
{ id: '3', name: 'toy3', desc: 'desc toy3' },
{ id: '4', name: 'toy4', desc: 'desc toy4' },
]
export const ToysListPage = () => {
return (
<View>
<FlatList
data={toys}
keyExtractor={(item) => item.id}
renderItem={({ item }) => {
const { id, name, desc } = item;
return (
<TouchableOpacity
onPress={() => navigation.navigate('ToyDetailsPage', { name, desc })}
>
<ToyCard
id={id}
name={name}
desc={desc}
/>
</TouchableOpacity>
);
}}
/>
</View>
)
}
It's a very simple example, but I really hope it can help you with what you plan to do.

Functional components cannot be given refs with react-native-rich-editor on Expo

I am trying to use this package :
https://www.npmjs.com/package/react-native-pell-rich-editor
And I have the following code :
import { useState, useRef } from "react";
import { RFPercentage } from "react-native-responsive-fontsize";
import BottomSheet from "#gorhom/bottom-sheet";
import {
StyleSheet,
View,
Text,
Platform,
KeyboardAvoidingView,
SafeAreaView,
ScrollView,
} from "react-native";
import {
actions,
RichEditor,
RichToolbar,
} from "react-native-pell-rich-editor";
export default function Publish() {
const postSheetRef = useRef(null);
const richText = useRef();
const snapPoints = ["90%"];
};
return (
<>
<View style={styles.container}>
<View style={styles.publishHeader}>
<Text style={styles.titleHeader}>Publier un post</Text>
</View>
<BottomSheet
ref={postSheetRef}
snapPoints={snapPoints}
handleIndicatorStyle={{ display: "none" }}
>
<View style={styles.main}>
<SafeAreaView>
<ScrollView>
<RichEditor
ref={richText}
onChange={(descriptionText) => {
console.log("descriptionText:", descriptionText);
}}
/>
</ScrollView>
<RichToolbar
editor={richText}
actions={[
actions.setBold,
actions.setItalic,
actions.setUnderline,
actions.heading1,
]}
iconMap={{
[actions.heading1]: ({ tintColor }) => (
<Text style={[{ color: tintColor }]}>H1</Text>
),
}}
/>
</SafeAreaView>
</View>
</BottomSheet>
</View>
</>
);
}
I get this error :
Functional components cannot be given refs
It concerns the Rich Editor component.
But I just followed the documentation, so I can't understand why.
I use ref with the Bottom sheet component and it works perfectly.
I am working with Expo.
How can I solve this ? This is the only package I have found that is compatible with Expo so I need to make it work ^^.
<RichEditor
ref={(r)=>{richText.current = r}}
onChange={(descriptionText) => {
console.log("descriptionText:", descriptionText);
}}
/>

Expo / TouchableOpacity onPress() not responding

I'm trying to reproduce a pomodoro app from an online course.
I've been looking at my code for several hours, going back and forth with the course, but can't find my error.
TouchableOpacity or Pressable used in RoundedButton component doesn't trigger onPress()
I tried different command to test if my logic with setStarted was flawe, without success.
Anyone can help me find my bug ?
Here is the link to the expo : https://snack.expo.dev/#battlepoap/focus-time
RoundedButton.js:
import React from 'react';
import { TouchableOpacity, Text, StyleSheet, View } from 'react-native';
export const RoundedButton = ({
style = {},
textStyle = {},
size = 125,
...props
}) => {
return (
<View>
<TouchableOpacity style={[styles(size).radius, style]}>
<Text style={[styles(size).text, textStyle]}>{props.title}</Text>
</TouchableOpacity>
</View>
);
};
const styles = (size) =>
StyleSheet.create({...});
Example with Focus.js where we add a task by pressing on the RoundedButton :
import React, { useState } from 'react';
import { View, Text, StyleSheet } from 'react-native';
import { TextInput } from 'react-native-paper';
import { RoundedButton } from '../../components/RoundedButton';
import { fontSizes, spacing } from '../../utils/sizes';
export const Focus = ({ addSubject }) => {
const [tempSubject, setTempSubject] = useState(null);
return (
<View style={styles.container}>
<View style={styles.titleContainer}>
<Text style={styles.title}>What do you want to focus on ?</Text>
<View style={styles.inputContainer}>
<TextInput
style={{ flex: 1, marginRight: spacing.sm }}
onSubmitEditing={({ nativeEvent }) => {
setTempSubject(nativeEvent.text);
}}
/>
<RoundedButton
size={50}
title="+"
onPress={() => addSubject = tempSubject}
/>
</View>
</View>
</View>
);
};
sic In the additional files there was ....
One thing we however need to add in order to make sure the button triggers is the onPress method. This method allows us to trigger the touch event of the user. Don't forget it!!
<TouchableOpacity style={[styles(size).radius, style]}>
<Text
style={[styles(size).text, textStyle]}
onPress={props.onPress}>
{props.title}
</Text>
</TouchableOpacity>

FlatList inside ScrollView doesn't scroll

I've 4 FlatLists with maxHeight set to 200 inside a ScrollView.
<ScrollView>
<FlatList/>
<FlatList/>
<FlatList/>
<FlatList/>
</ScrollView>
and when I try to scroll a FlatList, it doesn't scroll but the ScrollView scrolls. How do I fix this issue ?
Full Source Code
import { Component, default as React } from 'react';
import { FlatList, ScrollView, Text } from 'react-native';
export class LabScreen extends Component<{}> {
render() {
return (
<ScrollView>
{this.renderFlatList('red')}
{this.renderFlatList('green')}
{this.renderFlatList('purple')}
{this.renderFlatList('pink')}
</ScrollView>
);
}
getRandomData = () => {
return new Array(100).fill('').map((item, index) => {
return { title: 'Title ' + (index + 1) };
});
};
renderFlatList(color: string) {
return (
<FlatList
data={this.getRandomData()}
backgroundColor={color}
maxHeight={200}
marginBottom={50}
keyExtractor={(item, index) => index.toString()}
renderItem={({ item }) => <Text>{item.title}</Text>}
/>
);
}
}
snack.expo link
We can use the built-in nestedscrollenabled prop for the children FlatList/ScrollView components.
<FlatList nestedScrollEnabled />
This is only required for Android (Nested scrolling is supported by default on iOS).
I was having a very similar issue until I came across an almost complete solution in a very helpful comment on one of the GitHub issues for the react-native project: https://github.com/facebook/react-native/issues/1966#issuecomment-285130701.
The issue is that the parent component is the only one registering the scroll event. The solution is to contextually decide which component should actually be handling that event based on the location of the press.
You'll need to slightly modify your structure to:
<View>
<ScrollView>
<View>
<FlatList />
</View>
<View>
<FlatList />
</View>
<View>
<FlatList />
</View>
<View>
<FlatList />
</View>
</ScrollView>
</View>;
The only thing I had to change from the GitHub comment was to use this._myScroll.contentOffset instead of this.refs.myList.scrollProperties.offset.
I've modified your fully working example in a way that allows scrolling of the inner FlatLists.
import { Component, default as React } from "react";
import { View, FlatList, ScrollView, Text } from "react-native";
export default class LabScreen extends Component<{}> {
constructor(props) {
super(props);
this.state = { enableScrollViewScroll: true };
}
render() {
return (
<View
onStartShouldSetResponderCapture={() => {
this.setState({ enableScrollViewScroll: true });
}}
>
<ScrollView
scrollEnabled={this.state.enableScrollViewScroll}
ref={(myScroll) => (this._myScroll = myScroll)}
>
{this.renderFlatList("red")}
{this.renderFlatList("green")}
{this.renderFlatList("purple")}
{this.renderFlatList("pink")}
</ScrollView>
</View>
);
}
getRandomData = () => {
return new Array(100).fill("").map((item, index) => {
return { title: "Title " + (index + 1) };
});
};
renderFlatList(color: string) {
return (
<View
onStartShouldSetResponderCapture={() => {
this.setState({ enableScrollViewScroll: false });
if (
this._myScroll.contentOffset === 0 &&
this.state.enableScrollViewScroll === false
) {
this.setState({ enableScrollViewScroll: true });
}
}}
>
<FlatList
data={this.getRandomData()}
backgroundColor={color}
maxHeight={200}
marginBottom={50}
keyExtractor={(item, index) => index.toString()}
renderItem={({ item }) => <Text>{item.title}</Text>}
/>
</View>
);
}
}
Hopefully you find this useful!
This is the simplest answer that requires zero configuration.. and it works like a charm
<ScrollView horizontal={false}>
<ScrollView horizontal={true}>
<Flatlist
....
....
/>
</ScrollView>
</ScrollView>
I fixed my problem with nested FlatList not being able to scroll items on android by simply importing FlatList
import { FlatList } from 'react-native-gesture-handler';
If this would not work, also try to import ScrollView.
import { ScrollView } from 'react-native';
// OR
import { ScrollView } from 'react-native-gesture-handler';
You need to play around with these imports, at least it worked in my case.
Try to set the FlatList as nested
nestedScrollEnabled={true}
Using View with a flex:1 instead of ScrollView worked for me.
Use map instead of Flatlist, same result and don't break the application
Minha conta
{
buttonsProfile.map(button => (
<ArrowButton
key={button.key}
title={button.title}
iconName={button.icon}
toogle={button.toogle}
onPress={() => {navigation.navigate(button.route)}}
/>
))
}
The better answer is to put a horizontal ScrollView inside of the other ScrollView and then the FlatList

Element type is invalid: Expected String

I checked all code but didn't found the mistake which can cause such a strange error. As you see it's exported while it says you likely forget to export. Here is the code with the full list of imports:
import "expo";
import React from "react";
import {Image, TouchableHighlight} from "react-native";
import {
Content,
Left,
Right,
Icon,
CardItem,
Card,
Button,
Text,
Body,
Row,
Col,
Grid,
Thumbnail,
ScrollView
} from "native-base";
import {dataRow1,dataRow2,dataRow3,dataRow4} from "../data/HomeData";
import { primary, secondary, grey } from '../styles/variables';
const HomeContent = props => {
return (
<Content>
<ScrollView horizontal>
{dataRow1.map((item, idx) => {
return <CardItemContainer {...props} key={idx} item={item} />;
})}
</ScrollView>
<ScrollView horizontal>
{dataRow2.map((item, idx) => {
return <CardItemContainer {...props} key={idx} item={item} />;
})}
</ScrollView>
<ScrollView horizontal>
{dataRow3.map((item, idx) => {
return <CardItemContainer {...props} key={idx} item={item} />;
})}
</ScrollView>
<ScrollView horizontal>
{dataRow4.map((item, idx) => {
return <CardItemContainer {...props} key={idx} item={item} />;
})}
</ScrollView>
</Content>
);
};
const CardItemContainer = ({item, navigation}) => {
return (
<Card style={{marginBottom: 10}}>
<TouchableHighlight onPress={() => navigation.navigate("Items")}>
<CardItem cardBody>
<Image
source={item.image}
style={styles.img}
/>
</CardItem>
</TouchableHighlight>
<CardItem>
<Text style={{color:grey}}> {item.title} </Text>
</CardItem>
</Card>
);
};
const styles = {
img:{
height: 200,
width: null,
flex: 1
},
}
export default HomeContent;
What can cause it and what is wrong? Can you help me please to solve this issue?
Thanks in advance!
It looks like you have not imported any html tag reference from react-native. Every HTML tag is a element.
Let's suppose if you have Text tag then you must import it like import { Text} from react-native .
You should import ScrollView from react-native instead of native-base.
After removing ScrollView from native base import's line include it in react-native's import line like this:
import {Image, TouchableHighlight, ScrollView} from "react-native";