React Native TextInput onFocus does not allow onPress of other child components - reactjs

Nested TextInput component does not allow other components' onPress function to be called. Only when the TextInput is not focused, the onPress works fine.
React Native Version : 0.66.3
Here is my code
export const Component = (props): JSX.Element {
const { searchText, onChangeSearchText, onClearSearchText } = props
const [searchViewFocused, setSearchViewFocused] = useState<boolean>(false)
const searchIcon = searchViewFocused ? "searchActive" : "searchInactive"
const cancelIcon = searchViewFocused ? "cancelActive" : "cancelInactive"
return (
<View style={styles.searchBoxContainer}>
<View style={[styles.searchBox, searchViewFocused && styles.searchBarActive]}>
<Icon styles={styles.searchIcon} icon={searchIcon} />
<TextInput
style={styles.searchInput}
onChangeText={onChangeSearchText}
value={searchText}
onFocus={() => setSearchViewFocused(true)}
onBlur={() => setSearchViewFocused(false)}
autoCompleteType={"off"}
numberOfLines={1}
/>
{searchText !== "" && (
<Pressable style={styles.clearIcon} onPress={onClearSearchText}>
<Icon icon={cancelIcon} />
</Pressable>
)}
</View>
</View>
)
})
Attached are the images of
Expected.
VS
The issue
When the cross icon is pressed on focused state, the textInput is unfocused rather What I am trying to achieve is that the search text gets cleared & the input remains focused.
Note: The onPress works perfectly fine when input is not focused
Any help is appreciated. Thanks!
PS: Tried TouchableOpacity & also I have tried wrapping the components inside a ScrollView to use keyboardShouldPersistTaps='handled' as mentioned in one of the SO answer but to no success.

Found a workaround to this is to wrap the whole component into a ScrollView and adding the prop keyboardShouldPersistTaps='handled'
Previously I was making the View inside the Component as ScrollView and adding keyboardShouldPersistTaps='handled' which did not work
export const Component = (props): JSX.Element {
...
return (
<ScrollView contentContainerStyle={styles.searchBoxContainer}
keyboardShouldPersistTaps='handled'>
...
</ScrollView>
)
})
The key was to wrap the entire component inside the ScrollView,
Here's what worked:
<ScrollView keyboardShouldPersistTaps='handled'>
<Component {...props}/>
</ScrollView>
Guess this was a silly mistake, but worth pointing out!

You're setting the focus of the entire component based on whether the TextInput has focus. Since the clear button is outside the text input, pressing it causes the component to lose focus.
One solution is to store the TextInput instance in a ref. When the clear button is pressed, you can refocus the text input. I've copied your component below and added some new lines, which are marked in comments.
export const Component = (props): JSX.Element {
const { searchText, onChangeSearchText, onClearSearchText } = props
const textInputRef = useRef(null); // new
const [searchViewFocused, setSearchViewFocused] = useState<boolean>(false)
const searchIcon = searchViewFocused ? "searchActive" : "searchInactive"
const cancelIcon = searchViewFocused ? "cancelActive" : "cancelInactive"
const onClear = () => {
onClearSearchText();
textInputRef.current?.focus();
} // new
return (
<View style={styles.searchBoxContainer}>
<View style={[styles.searchBox, searchViewFocused && styles.searchBarActive]}>
<Icon styles={styles.searchIcon} icon={searchIcon} />
<TextInput
ref={textInputRef} // new
style={styles.searchInput}
onChangeText={onChangeSearchText}
value={searchText}
onFocus={() => setSearchViewFocused(true)}
onBlur={() => setSearchViewFocused(false)}
autoCompleteType={"off"}
numberOfLines={1}
/>
{searchText !== "" && (
<Pressable style={styles.clearIcon} onPress={onClear}> // calls new function
<Icon icon={cancelIcon} />
</Pressable>
)}
</View>
</View>
)
})

Related

Issues with Modals React Native

everyone. I'm new in this new tecnology and I'm having a little problem with modals.
I'm programming a modal that only shows when a condition is true, in the first click it's all ok, but in the second the modal won't show, then when I click again, the window appears again, and continue successively.
The code works as follow: I have 3 states, each represent a different text to show in the modal. Then, I have a modal, that recieves a message (to print) and the state to identifies what state i need to show. And finally, in the main, I have a condition who calls the modal and show it ONLY if the state is true.
I checked the states with console.log and the only issue i find is that in some cases, the state is set in true, when is false, but i don't know why!
Thanks for read!
Here i detach part of the code.
Declare of the states in the main
const [modalVisibleEnTermino, setModalVisibleEnTermino] = useState(false)
const [modalVisibleFueraTermino, setModalVisibleFueraTermino] = useState(false)
const [modalVisibleRefuerzoTermino, setModalVisibleRefuezoTermino] = useState(false)
Condtition in main to show the apropiate state of modal
const setModal = () => {
vacunas ===2 && (days>6 && days<150) ? setModalVisibleEnTermino(true) : setModalVisibleFueraTermino(true)
if(vacunas == 3)
setModalVisibleRefuezoTermino(true)
}
Function to reset the states after showing modal (IDK if its ok)
const restoreModals = () => {
if (modalVisibleEnTermino) setModalVisibleEnTermino(false)
if (modalVisibleFueraTermino) setModalVisibleFueraTermino(false)
if (modalVisibleRefuerzoTermino) setModalVisibleRefuezoTermino(false)
}
After set the modal, I checked what state it's true, and I passed the values in this condition
{modalVisibleEnTermino && <MyModal
message={"Mensaje1"}
state={modalVisibleEnTermino}
/>
}
{modalVisibleFueraTermino && <MyModal
message={"Mensaje2."}
state={modalVisibleFueraTermino}
/>
}
{modalVisibleRefuerzoTermino && <MyModal
message={"Mensaje3."}
state={modalVisibleRefuerzoTermino}
/>
}
Modal
export const MyModal = (props) => {
const [visibleModal, setVisibleModal] = useState(props.state)
return (
<Modal visible={visibleModal}
transparent={true}
animationType='slide'
>
<View style={styles.container}>
<Text style={styles.titulo_label}>¡IMPORTANTE!</Text>
<Text style={styles.info_label}>{props.message}</Text>
<TouchableOpacity style={styles.button_aceptar} onPress={() => setVisibleModal(false)}>
<Text style={styles.text_button}>Aceptar</Text>
</TouchableOpacity>
</View>
</Modal>
);
Add a restore property to your MyModal component
{modalVisibleEnTermino && <MyModal
message={"Mensaje1"}
state={modalVisibleEnTermino}
restore={restoreModals}
/>
}
{modalVisibleFueraTermino && <MyModal
message={"Mensaje2."}
state={modalVisibleFueraTermino}
restore={restoreModals}
/>
}
{modalVisibleRefuerzoTermino && <MyModal
message={"Mensaje3."}
state={modalVisibleRefuerzoTermino}
restore={restoreModals}
/>
}
In the MyModal Component remove the useState and pass the props.state to visible and use the props.restore on the onPress callback like so
export const MyModal = (props) => {
return (
<Modal visible={props.state}
transparent={true}
animationType='slide'
>
<View style={styles.container}>
<Text style={styles.titulo_label}>¡IMPORTANTE!</Text>
<Text style={styles.info_label}>{props.message}</Text>
<TouchableOpacity style={styles.button_aceptar} onPress={() => props.restore()}>
<Text style={styles.text_button}>Aceptar</Text>
</TouchableOpacity>
</View>
</Modal>
);
In your original MyModal Component, by passing the props.state to useState, you are creating a new state within that component. Setting onPress to setVisibleModal(false) will not change the states of modalVisibleEnTermino, modalVisibleFueraTermino, and modalVisibleRefuerzoTermino. By passing restoreModals as a prop to MyModal you can change the states in the Main component from inside MyModal.
One more thing, remove the if statements in your restoreModals function
const restoreModals = () => {
setModalVisibleEnTermino(false)
setModalVisibleFueraTermino(false)
setModalVisibleRefuezoTermino(false)
}

How can I open BottomSheetModal (#gorhom/bottom-sheet) in different component, using React-Native (Expo)

I am using https://gorhom.github.io/react-native-bottom-sheet/.
I was wondering how can I open "BottomSheetModal" in a different file e.g Navbar Component.
This is what my code looks like at the moment to open the Bottom Sheet inside of the same component.
const BottomSheetModal: FC = () => {
const bottomSheetModalRef = useRef<BottomSheet>(null);
const snapPoints = useMemo(() => ["25%", "50%"], []);
const handlePresentModalPress = useCallback(() => {
bottomSheetModalRef.current?.present();
}, []);
return (
<>
<Button title="Test" onPress={() => handlePresentModalPress()} />
<BottomSheet
index={1}
style={{ ...shadows.bottomSheet }}
ref={bottomSheetModalRef}
snapPoints={snapPoints}>
<View style={styles.container}>
<Text>Awesome 🎉</Text>
</View>
</BottomSheet>
</>
);
};
So how can I use the opening code of the Bottom Sheet inside of my Navbar Component?
Navbar Component:
// Open BottomSheet here
<TouchableWithoutFeedback onPress={() => openBottomSheet()}>
<View>
<Image
style={styles.avatar}
source={{
uri: "https://lumiere-a.akamaihd.net/v1/images/character_themuppets_kermit_b77a431b.jpeg?region=0%2C0%2C450%2C450",
}}
/>
</View>
</TouchableWithoutFeedback>
Thank you!
I found out to do this, incase anyone comes across this question, I'll post it here!
So what you have to do is pass the ref to the bottom sheet component. So in the Navbar component I created the ref for the bottom sheet, and then passed it into the bottom sheet.
Navbar:
// Create Ref
const userBottomSheetRef = useRef<BottomSheetModal>(null);
// Pass ref into the bottom sheet component
<BottomSheet ref={userBottomSheetRef} snapPoints={["30%"]}/>
Then inside the bottom sheet component you forward the ref using a react function, and then pass it in as normal:
<BottomSheetModal ref={ref} >
<BottomSheetScrollView>
<View style={styles.container}>{children}</View>
</BottomSheetScrollView>
</BottomSheetModal>

How to send values without navigating to child component

good morning
I wanted to send some array data to child component , the purpose of this component is to make my code in one page shorter
this page i named it homepage, inside homepage there are multiple charts
import Chart01 from ../components/chart01
import Chart02 from ../components/chart02
import Chart03 from ../components/chart03
and in this homepage there will be buttons that (I'm planning to) send value to those 3 charts
const [FirstValue,setFirstValue] = useState(0)
return(
<View>
<View>
<Button onPress={()=>setFirstValue(1)}
<Button onPress={()=>setFirstValue(2)}
<Button onPress={()=>setFirstValue(3)}
</View>
<Chart01/>
<Chart02/>
<Chart03/>
</View>
)
How do i send the FirstValue from HomePage to Chart01 Chart02 Chart03 ?
and in Chart01, how do I call value that sent from HomePage ?
Thank you
In home you have pass like below,
<Chart01 value={FirstValue}/>
<Chart02 value={FirstValue}/>
<Chart03 value={FirstValue}/>
In Chart01,02,03 definition, you have to read props.
const Chart01 = (props) => {
const { value } = props;
// someting
};
Pass the values as props to the chart components.
const [firstValue, setFirstValue] = useState(0);
return(
<View>
<View>
<Button onPress={() => setFirstValue(1)}
<Button onPress={() => setFirstValue(2)}
<Button onPress={() => setFirstValue(3)}
</View>
<Chart01 value={firstValue} />
<Chart02 value={firstValue} />
<Chart03 value={firstValue} />
</View>
);
Within the chart components you can access the passed value from the value prop (or whatever you name it). For example:
props.value

React Native no models show

I would like to show modal when the user enabled the switch or disabled. what should i do to achieved what i want? this is my current code. I am new in this technology, I hope you all understand , Please bear with me. thanks
import {SimpleModal} from '../../components/modal/Modal';
const [isModalVisible, setisModalVisible] = useState(false);
const changeModalVisible=(bool) =>{
setisModalVisible(bool);
}
const confirmDelete = (id,status) =>{
console.log("changeModalVisible: ",changeModalVisible(true))
if (status == 0){
if (changeModalVisible(false)) {
handleDelete(id,status);
} else {
alert("error")
}
}else{
if (changeModalVisible(true)) {
handleActivate(id,status);
} else {
}
}
}
<FlatList
keyExtractor={(item, index) =>index.toString()}
data={documentlists}
renderItem={({ item }) => (
<DocumentLocation
key={item.name}
status={item.status}
editHandler={handleEdit}
pressHandler={confirmDelete}
/>
)}
/>
<Modal
transparent={true}
animationType='fade'
visible={isModalVisible}
nRequestClose={() => changeModalVisible(false)}
></Modal>
<SimpleModal/>
component/model/Model.js
const SimpleModal = () => {
return (
<TouchableOpacity
disabled={true}
style={formStyles.container}
>
<View>
<View>
<Text>
Sample modal header
</Text>
<Text>
Sample modal description
</Text>
</View>
</View>
</TouchableOpacity>
)
}
work flow, if the client switch the toggle popup modal will appear,
in the modal, i want two button that is ok and cancel, if the user click the ok, the data will updated and if cancel, no updated data, note the functionality of updating data is already working, my problem is the modal , please refer to this image
As per your comment this is the flow you expect.
User clicks switch in flatlist
If the value is false the status is updated
If value is true, a confirmation Modal is shown.
There are some changes required
renderItem={({ item,index }) => (
<DocumentLocation
key={item.name}
status={item.status}
editHandler={handleEdit}
pressHandler={(value)=>confirmDelete(value,index)}
/>
)}
Here the value should come from the switch
instead of using the showModalVisible have a state to manage the selectedIndex
const [selectedIndex, setSelectedIndex] = useState(-1);
If an item is changed to true we set the selectedIndex
We use that to control the visibility of the modal
<Modal visible={selectedIndex > -1} onRequestClose={() => setSelectedIndex(-1)}>
<Text>Approve?</Text>
<Button
title="Yes"
onPress={() => {
updateSelectedItem(selectedIndex, true);
setSelectedIndex(-1);
}}
/>
<Button title="No" onPress={() => setSelectedIndex(-1)} />
</Modal>
The buttons in the Modal will rest the selected index or update the array.Something like the below function.
const updateSelectedItem = (index, value) => {
const updatedData = [...data];
updatedData[index].active = value;
setData(updatedData);
};
Sample code
https://snack.expo.io/#guruparan/verifypopup
You should enclose your SimpleModal component in the Modal, like this -
<Modal
transparent={true}
animationType='fade'
visible={isModalVisible}
nRequestClose={() => changeModalVisible(false)}
>
<SimpleModal/>
</Modal>
for modals use https://github.com/jeremybarbet/react-native-modalize
this library saved me a lot of days and pain developing modals in my app
if you are not using typescript, change the ref from
const modalizeRef = useRef<Modalize>(null);
to
const modalizeRef = useRef(null);

Hide all other same child component in click of one child component

I created a custom Picker component using Touchable Opacity and Picker. On click of touchable opacity component, the related Picker toggles. I am using this component multiple time in one parent with different data.
Picker's toggle state isPickerOpen defined and used in the child component to reduce the complexity of state management in the parent component. I'm required to click on each touchable opacity to show and hide its related Picker.
Here is the code of child component:
function CustomPicker(props) {
const {listItem, callbackParent} = props;
const [isPickerOpen, setPickerOpen] = useState(false);
const pickerListItem = listItems.map((item, i) => {
return <Picker.Item key={i} label={item.text} value={item.value} />
});
return (
<View>
<TouchableOpacity style={styles.pickerButton}
onPress={() => setPickerOpen(!isPickerOpen)}>
<Text>Title</Text>
</TouchableOpacity>
{isPickerOpen && <Picker onValueChange={(value) =>
callbackParent(value)}>
{pickerListItem}
</Picker>
}
</View>
);
}
And the parent component fraction:
<SelectionPicker listItems={list1} onPickerSelection={handleChange} />
<SelectionPicker listItems={list2} onPickerSelection={handleChange} />
<SelectionPicker listItems={list3} onPickerSelection={handleChange} />
How to manage the child component's state on click of touchable opacity, to hide all other open picker, and show just one related picker?
You need to add an external variable to have another form of validation for the Picker component, something like this:
function CustomPicker(props) {
const {
listItems,
onPickerClick,
onPickerSelection,
selectable = true,
name,
} = props;
//
const [isPickerOpen, setPickerOpen] = useState(false);
const pickerListItem = listItems.map((item, i) => {
return <Picker.Item key={i} label={item.text} value={item.value} />
});
return (
<View>
<TouchableOpacity
style={styles.pickerButton}
onPress={() => {
// calling our picker selection function
onPickerClick(name)
setPickerOpen(!isPickerOpen)
}}
>
<Text>Title</Text>
</TouchableOpacity>
{(isPickerOpen && selectable) && <Picker onValueChange={(value) =>
onPickerSelection(value, name)}>
{pickerListItem}
</Picker>
}
</View>
);
}
// Our selection handling function
handleSelection(name) {
this.setState({
currentPicker: name,
})
}
// Adding unique names, a validation prop, and a selection method
<SelectionPicker
name="someNameA"
selectable={'someNameA' === currentPicker}
onPickerClick={handleSelection}
onPickerSelection={handleChange}
listItems={list1}
/>
<SelectionPicker
name="someNameB"
selectable={'someNameB' === currentPicker}
onPickerClick={handleSelection}
onPickerSelection={handleChange}
listItems={list2}
/>
<SelectionPicker
name="someNameC"
selectable={'someNameC' === currentPicker}
onPickerClick={handleSelection}
onPickerSelection={handleChange}
listItems={list3}
/>

Resources