Nested TouchableOpacity Parent onPress not working - javascript

i had this issue where i'm trying to make sure the parent's onPress is triggered, but it wont
im trying to create a custom touchableOpacity component that able can be reusable, that wrap other Component so it can decide if the children can be shown or not and decide/alter what happen when the children component is pressed.
const CustomTouchable = (children, onPress) => {
function handleOnPress = () => {
if(validation){
onPress();
}
}
return <TouchableOpacity onPress={handleOnPress}>{children}</TouchableOpacity>
}
const MainComponent = () => {
function onPress = () => {console.log('test')}
<CustomTouchable onPress={onPress}>
<TouchableOpacity style={styles.button}>
<Text>Press Here</Text>
</TouchableOpacity>
</CustomTouchable>
}
but the parent onPress is not triggered, how can i trigger it?

This is because the touch event is received by the children and not the parent. Assign following prop to your Child Component
pointerEvents={"none"}

Make the second TouchableOpacity disabled like this
<TouchableOpacity onPress={onPress}>
<TouchableOpacity
disabled
style={styles.button}
>
<Text>Press Here</Text>
</TouchableOpacity>
</TouchableOpacity>

Related

React Native, how to change display of touchable opacity to visible when clicking on another, and invisible when you click again?

As the title suggests, I am struggling to find a way to make my touchable opacities have a display of none by default (well, I suppose that is easy enough with a styling of display: none), but I'm not able to figure out how to toggle that using a touchable opacity.
In my head, the logic is to have the state change from true to false onpress, and false is visible while true is invisible. However, I can't muster up the knowledge to code it out. Here is what I have so far, more info below code:
import React, {useState} from 'react';
import { KeyboardAvoidingView, StyleSheet, Text, View, TextInput, TouchableOpacity, Keyboard, ImageBackground } from 'react-native';
import Task from './components/task';
const plus = {uri: 'https://media.discordapp.net/attachments/639282516997701652/976293252082839582/plus.png?width=461&height=461'};
const done = {uri: 'https://media.discordapp.net/attachments/736824455170621451/976293111456231434/done.png?width=461&height=461'};
const exit = {uri: 'https://media.discordapp.net/attachments/639282516997701652/976293251759898664/exit.png?width=461&height=461'};
const cog = {uri: 'https://media.discordapp.net/attachments/639282516997701652/976293672884789288/cog.png?width=461&height=461'};
function App() {
const [task, setTask] = useState();
const [taskItems, setTaskItems] = useState([]);
const buttons = {plus, done, exit, cog}
const [selected, setSelected] = useState(buttons.plus)
const [done, setDone] = useState(buttons.plus);
const openMenu = () => {
setSelected(buttons.exit);
//Make 'done' and 'cog' TouchableOpacity(s) visible. Click again and they become invisible.
//this.setState({ visible : !this.state.visible}) This makes visible invisible if not invisible already.
//visible should be the name of a state.
{/*handleAddTask();*/}
}
const handleAddTask = () => {
setDone(buttons.done);
Keyboard.dismiss();
setTaskItems([...taskItems, task]); {/*Puts out everything in the taskItems as a new array then appends the new task to it */}
setTask(null);
setSelected(buttons.plus) //Makes exit button go back to plus because it shows that its finished. DO same for display none for extended buttons when I figure it out.
}
const completeTask = (index) => {
let itemsCopy = [...taskItems];
itemsCopy.splice(index, 1);
setTaskItems(itemsCopy);
}
return (
<View style={styles.container}>
{/*Tasks*/}
<View style={styles.tasksWrapper}>
<Text style={styles.sectionTitle}>Tasks</Text>
<View style={styles.items}>
{/*This is where tasks go*/}
{
taskItems.map((item, index) => {
return (
<TouchableOpacity key={index} onPress={() => completeTask(index)}>
<Task text={item} />
</TouchableOpacity>
)
})
}
</View>
</View>
{/*Write a task*/}
<KeyboardAvoidingView behavior={Platform.OS === "ios" ? "padding" : "height"} style={styles.writeTaskWrapper}>
<TextInput style={styles.input} placeholder={'Write a task'} value={task} onChangeText={text => setTask(text)}/>
<View style={styles.buttonRow}>
<TouchableOpacity onPress={() => openConfig()}>
{/* Opens config for creation (i.e. calendar, timer, repetition, etc). */}
<View style={styles.addWrapper}>
<ImageBackground source={buttons.cog} alt='button' resizeMode="cover" style={styles.plusButton} />
</View>
</TouchableOpacity>
<TouchableOpacity onPress={() => handleAddTask()}>
{/* Done (check) button which does handleAddTask. */}
<View style={styles.addWrapper}>
<ImageBackground source={buttons.done} alt='button' resizeMode="cover" style={styles.plusButton} />
</View>
</TouchableOpacity>
<TouchableOpacity onPress={() => openMenu()}>
{/* Onpress opens menu, then shows exit button to go back and revert menu to one button. */}
<View style={styles.addWrapper}>
<ImageBackground source={selected} alt='button' resizeMode="cover" style={styles.plusButton} />
</View>
</TouchableOpacity>
</View>
</KeyboardAvoidingView>
</View>
);
}
The three touchable opacities at the bottom are what I'm trying to change. The first two should by default be invisible, and I think I can do that by assigning useState(false) for them and false should make their display none. Then on the click of the third touchable opacity, it changes their previous state => !previous state.
However, I'm not sure how to code this out and am quite confused. Any help is appreciated. Thanks!
This can be done using conditional rendering. You will either need a state for each of the buttons or a state that holds an array.
Here is a minimal example which works in general.
function App() {
const [isAVisible, setAVisible] = useState(true);
const [isBVisible, setBVisible] = useState(false);
return (
<View>
{isAVisible && (
<TouchableOpacity onPress={() => setIsBVisible(prev => !prev)}}>
<Text>Toggle Visibility of B</Text>
</TouchableOpacity>
)}
{isBVisible && (
<TouchableOpacity onPress={() => setIsAVisible(prev => !prev)}}>
<Text>Toggle Visibility of A</Text>
</TouchableOpacity>
)}
</View>
)
}
The above creates two TouchableOpacity. The first toggles the visibility of the second one, and the second one toggles the visibility of the first one. Notice, that the default state of the second one is set to false, thus it will be not be visible on first render.

How to check if press outside a component in react-native?

I did a custom select but I have the problem to close it if I press outside the select or options. basically the "button" is a TouchableOpacity and when I click on it there appears the list of options. But now I can close it only by choosing one option or clicking back on the select button. Is there a way to check whether I click outside the TouchableOpacity or not? In simple react you can give an Id to the component and check it onClick event to see what you have clicked. Or you can use react's useRef hook which doesn't seem to work with react-native. I have this code (simplified):
const [isOpen, setIsOpen] = useState(false)
const toggle = () => { setIsOpen(!isOpen)}
//...
return (<View>
<TouchableOpacity onPress={toggle}>
<Text>Open select</Text>
</TouchableOpacity>
<View>
{isOpen && options.map(({value, label}) => <View key={value} onPress={toggle}>{label}</View>)}
</View>
</View>)
As you can see you can call toggle only if you press the select button or an option. I want to call setIsOpen(false) when I click outside the TouchableOpacity box.
Is there a way or library to do it?
First of all correct usage for toggle function is
setIsOpen(prevIsOpen => !prevIsOpen);
And regarding your question. Just wrap all screen into touchable component without any feedback.
const close = () => isOpen && setIsOpen(false);
return (
<TouchableWithoutFeedback onPress={close} style={{ flex: 1 }}>
<View>
<TouchableOpacity onPress={toggle}>
<Text>Open select</Text>
</TouchableOpacity>
<View>
{isOpen && options.map(({value, label}) => <View key={value} onPress={toggle}>{label}</View>)}
</View>
</View>
</TouchableWithoutFeedback>
);
you can use TouchableWithoutFeedback

Not working onPress in nested TouchableOpacity

Hi my custom component is wrapped in TouchableOpacity like this.
const profileOnClick = () => {
console.log('Card Clicked!');
};
export const InfluencerCard = props => {
const {influencer, navigation} = props;
return (
<TouchableOpacity onPress={() => profileOnClick()}>
<Card>
<Text>
{influencer.user.name}
</Text>
<Text>
{influencer.tags[0].name}
</Text>
</Card>
</TouchableOpacity>
);
};
In Homescreen
<ScrollView>
{data.categoriesForHome.map(category => (
<Layout key={category.id}>
<Text>
{category.name}({category.total})
</Text>
<ScrollView horizontal={true}>
{category.influencerProfiles.map(profile => (
<View key={profile.id}>
<InfluencerCard influencer={profile} />
</View>
))}
</ScrollView>
</Layout>
))}
</ScrollView>
When I clicked my custom component InfluencerCard, it doesn't do anything.
I wonder it is because that component is in other component, so parent component block clicking on custom component. Or calling onPress function is wrong.
But I tried without parent component, it was working.
What am I missing?
It was my mistake. The problem was not from code or components.
I use Card component from #ui-kitten/components and it implements TouchableOpacity behind the scene. So I don't need to wrap with TouchableOpacity again.So just do
<Card onPress={() => profileClick()}></Card>

Using a ref in a FlatList in React Native

I am still having trouble understanding ref's in React Native (and React in general). I am using functional component. I have a FlatList that has many items. How do I create a reference for a thing within an item like a Text or View component?
<FlatList
data={data}
renderItem={({ item }} => {
<View>
... lots of other stuff here
<TouchableOpacity onPress={() => _editITem(item.id)}>
<Text ref={(a) => 'text' + item.id = a}>EDIT</Text>
</TouchableOpacity>
</View>
}
/>
Then in _editItem I want to reference the Text component so that I can change its text from 'EDIT' to 'EDITING', or even change its style, or whatever.
_editPost = id => {
console.log(text + id)
}
I have tried...
FeedComponent = () => {
let editPosts = {}
<FlatList
data={data}
renderItem={({ item }} => {
<View>
... lots of other stuff here
<TouchableOpacity onPress={() => _editITem(item.id)}>
<Text ref={(a) => editPosts[item.id] = a}>EDIT</Text>
</TouchableOpacity>
</View>
}
/>
...and a few other things, but I think I might be way off so I could use some guidance.
Typically you don't use refs in react to update content like text. Content should be rendered based on the current props and state of your component.
In the case you describe you'll probably want to set some state in the parent component that then impacts the rendering of the item.
As a sidenote refs are used if you need to trigger a method on a child component like calling focus on a TextInput for example but not for imperatively updating component content.
In your case you'll want to update some state representing the current active item. Something like:
import React, {useState} from 'react';
FeedComponent = () => {
const [activeItem, setActiveItem] = useState(null);
<FlatList
data={data}
renderItem={({ item }} => {
return (
<View>
... lots of other stuff here
<TouchableOpacity onPress={() => setActiveItem(item.id)}>
{activeItem === item.id
? <Text>EDITING</Text>
: <Text>EDIT</Text>
}
</TouchableOpacity>
</View>
);
}
extraData={activeItem}
/>

Trigger onPress event on Child component within Parent component

I made a button component which I render in my Parent component. Been stuck for a while now on trying to fire a function on the onPress event of this child component that is used in my Parent.
I've been looking through some of the recommended questions and answers but I need some specific advice.
I simplified my code as much as possible, please have a quick look.
Thanks in advance!
// PARENT COMPONENT
export class Home extends Component {
constructor(props) {
super(props);
this.onPress = this.onPress.bind(this);
}
onPress = () => {
console.log("Hey");
};
render() {
return (
<View style={styles.container}>
<PrimaryButton text={"Sign up"} onPress={this.onPress} />
</View>
);
}
}
// CHILD COMPONENT
const PrimaryButton = ({ text }) => {
return (
<TouchableOpacity style={style.container} >
<Text style={style.text}>{text}</Text>
</TouchableOpacity>
);
};
export default PrimaryButton;
You need to pass onPress to TouchableOpacity as a prop. I don't know the props for TouchableOpacity but should be bound as either onPress or onClick. Event handlers always need to be passed to the root component (or as close to it as you can get, ie TouchableOpacity is from a 3rd party). Most, if not all, 3rd party components will have props for the proper events.
// CHILD COMPONENT
const PrimaryButton = ({ text, onPress }) => {
return (
<TouchableOpacity style={style.container} onClick={onPress} >
<Text style={style.text}>{text}</Text>
</TouchableOpacity>
);
};

Categories