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

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

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 pass Button component's title into a function in React Native

I want to pass the title of a React Native Button component into a neighbouring function. I am using React Native functional components only for this application.
Here's the component. I would like to pass the title of the button pressed by the user, which will be either 'English' or 'Arabic', into the function submitLanguageSelection so that I can then save that value into useLocalStorage(), a custom hook I wrote to handle AsyncStorage, so that the next time the user uses the app, their language choice will be persisted, and they will not be shown the ChooseYourLanguageScreen again.
All help appreciated, thank you.
const ChooseYourLanguageScreen = ({ navigation }) => {
const [saveData, storedValue, errorMessage] = useLocalStorage();
const [userSelectedLanguage, setUserSelectedLanguage] = React.useState('');
const submitLanguageSelection = () => {
//TODO: receive params from onPress
//TODO: save the data locally
//TODO: navigate to welcome screen
};
return (
<View style={styles.container}>
{errorMessage ? <Text>{errorMessage}</Text> : null}
<Text style={styles.text}>This is the Choose Your Language Screen</Text>
<View style={styles.buttons}>
<View>
<Button
title={'English'}
onPress={() => submitLanguageSelection()}
/>
</View>
<View>
<Button title={'Arabic'} onPress={() => submitLanguageSelection()} />
</View>
</View>
</View>
);
};
You can simply pass it to the function
<Button title={'Arabic'} onPress={() => submitLanguageSelection('Arabic')} />
And access like below
const submitLanguageSelection = (language) => {
console.log(language);
};
Getting data from a sibling component is an anti-pattern.
The source of the knowledge of the language options is the ChooseYourLanguageScreen component (as seems from your snippet), so it should hold the list of available languages. Having that, you can just iterate through them and render the appropriate components:
<View style={styles.buttons}>
{languages.map((language) => (
<View key={language}>
<Button
title={language}
onPress={() => submitLanguageSelection(language)}
/>
</View>
))}
</View>

Nested TouchableOpacity Parent onPress not working

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>

How can I set focus on the first TouchableHighlight component (or another one, given by ref for instance) inside the modal when modal is opened?

How can I set focus to the first (or any given) TouchableHighlight component inside the modal when it's opened?
I'm using D-pad/kayboard/TV Remote
Let's use the fragment of the react-native documentation modal example:
<View style={{marginTop: 22}}>
<Modal
animationType="slide"
transparent={false}
visible={this.state.modalVisible}
onRequestClose={() => {
Alert.alert('Modal has been closed.');
}}>
<View style={{marginTop: 22}}>
<View>
<TouchableHighlight>
<Text>Button 1</Text>
</TouchableHighlight>
<TouchableHighlight>
<Text>Button 2</Text>
</TouchableHighlight>
<TouchableHighlight
onPress={() => {
this.setModalVisible(!this.state.modalVisible);
}}>
<Text>Hide Modal</Text>
</TouchableHighlight>
</View>
</View>
</Modal>
<TouchableHighlight
onPress={() => {
this.setModalVisible(true);
}}>
<Text>Show Modal</Text>
</TouchableHighlight>
</View>
Navigation with D-pad works, but when the modal is opened, the first TouchableHighlight (button 1) is not focused, focus remains on the "Show Modal" button
Or, How can I set focus on the "Button 2" TouchableHighlight programatically?
TextInput for example has autoFocus, but TouchableHighlight no, if we are using only Touchable components inside a modal, I don;t know how to autoFocus them, or set implicitely it
Best Regards
Add hasTVPreferredFocus: true to the TouchableHighlight you want focused.
It is implemented on Android even though the documentation states that hasTVPreferredFocus is iOS-only.
Programmatically you can also force the focus by calling:
yourElementRef.setNativeProps({ hasTVPreferredFocus: true })
#mtkopone . is there any way to focus View component, i have a list which is inside View and i want to do some function calls when user focus on that View (onFocus,onBlur)
initialRef.current.setNativeProps({hasTVPreferredFocus: true});

In React Native, is it possible to call onPress while keyboard open [duplicate]

This question already has an answer here:
React Native - Select row on ListView when keyboard is open
(1 answer)
Closed 4 years ago.
The user needs to click twice on the FlatList item because autoFocus={true} for the <TextInput. At the first click the keyboard is hiding and next click calling onPress={this.GetItem.bind(this, item)}. Is there any option to call GetItem() on the first click instead of clicking twice.
Demo: https://snack.expo.io/ByJ_yWehM
export default class App extends Component {
GetItem (item) {
console.log(item);
Alert.alert(item);
}
render() {
return (
<View style={styles.container}>
<TextInput
autoFocus={true}
style={styles.paragraph}
keyboardType='web-search'
>
Change code in the editor and watch it change on your phone!
Save to get a shareable url.
</TextInput>
<Card title="Local Modules">
<View>
<TextInput
style={styles.searchField}
placeholder="Type here to translate!"
onChangeText={(text) => this.setState({text})}
/>
<FlatList
data={["akin","alike","close","comparable","like","similar","uniform","Allied","Analogous","Co-ordinate","Commensurate","akin","alike","close","comparable","like","similar","uniform","Allied","Analogous","Co-ordinate","Commensurate"]}
renderItem={({item}) => (
<Text
style={styles.listField}
onPress={this.GetItem.bind(this, item)}
>{item}</Text>
)}
/>
</View>
</Card>
</View>
);
}
}
The purpose of the component is giving autosuggestion in <FlatList> when user searching in <TextInput>
Adding keyboardShouldPersistTaps='handled' to your FlatList will prevent keyboard from being dismissed onPress.
<FlatList
keyboardShouldPersistTaps='handled'
data={["akin","alike","close","comparable","like","similar","uniform","Allied","Analogous","Co-ordinate","Commensurate","akin","alike","close","comparable","like","similar","uniform","Allied","Analogous","Co-ordinate","Commensurate"]}
renderItem={({item}) => (
<Text
onPress={this.GetItem.bind(this, item)}
>{item}</Text>
)}
/>
always also works as keyboardShouldPersistTaps value.
Official doc for keyboardShouldPersistTaps here

Categories