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}
/>
Related
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>
)
})
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)
}
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);
Im trying to pass parameters through to a component two levels deep.
I have 2 screens (MainScreen & UserProfileScreen) with a flat list on both screens, both flat lists use the same component EventCard in its renderItem. EventCard is made up of 3 three nested components EventCardHeader,EventCardBody & EventCardFooter. How do I pass certain arguements only from the UserProfileScreen? I have posted code below to give a better understanding of what I have.
MainScreen
<FlatList
data={this.state.events}
// Get the item data by referencing as a new function to it
renderItem={({item}) =>
<EventCard
openEventDetail={() => this.openEventDetail(item)}
{...item}
/>}
/>
UserProfileScreen
<FlatList
data={this.state.events}
// Get the item data by referencing as a new function to it
renderItem={({item}) =>
<EventCard
openEventDetail={() => this.openEventDetail(item)}
openEditEvent={() => this.openEditEvent(item)}
openDeleteEventAlert={() => this.openDeleteEventAlert(item)}
{...item}
/>}
/>
openEditEvent = (event) => {
this.props.navigation.navigate('EventFormScreen', {
event: event,
eventKey: this.state.eventKey,
editMode: true,
});
};
EventCard
export default class EventCard extends Component {
render() {
return (
<Card>
<EventCardHeader
eventOrganiserImage={this.props.eventOrganiserImage}
eventVenue={this.props.eventVenue}
openEditEvent={() => this.openEditEvent()}
/>
<EventCardBody
openEventDetail={() => this.props.openEventDetail()}
imageDownloadUrl={this.props.imageDownloadUrl}
/>
<EventCardFooter
openEventDetail={() => this.props.openEventDetail()}
eventName={this.props.eventName}
eventStartDate={this.props.eventStartDate}
eventVenue={this.props.eventVenue}
eventAddressLine1={this.props.eventAddressLine1}
eventAddressLine2={this.props.eventAddressLine2}
/>
</Card>
);
}
};
EvenCardHeader
export default class EventCardHeader extends Component {
render() {
return (
<CardSection style={styles.eventCardHeader}>
<Thumbnail small
style={styles.eventOrganiserImage}
source={{uri: this.props.eventOrganiserImage}}/>
<Text style={styles.eventPromoterName}>{this.props.eventVenue}</Text>
{!!this.props.openEditEvent &&
<Button onPress={() => this.props.openEditEvent()}>
Edit
</Button>
}
{!!this.props.openDeleteEventAlert &&
<Button onPress={() => this.props.openDeleteEventAlert()}>
Delete
</Button>
}
</CardSection>
);
}
}
I can see that because I have hardcoded the this.openEditEvent() function into my EventCard component that what's causing me the problem, because then the if statement in EventCardHeader that checks if the this.openEditEvent() exists always evaluates to true. Would someone be able to help show me the right way to do this? Thanks in advance for any help.
UPDATE:
Added in openEditEvent
Where is openEditEvent() declared? It should be in the parent component and passed as props to whatever children you need it in. You can continue to pass it as props from children to children.
EDIT:
Ok so you can pass openEditEvent as props like so:
<EventCard
openEditEvent = this.openEditEvent
openEventDetail={() => this.openEventDetail(item)}
openDeleteEventAlert={() => this.openDeleteEventAlert(item)}
{...item}
/>}
That function can be available in EventCard, and can then be passed AGAIN as props to another child component:
render() {
var openEditEvent = this.props.openEditEvent;
return (
<Card>
<EventCardHeader
eventOrganiserImage={this.props.eventOrganiserImage}
eventVenue={this.props.eventVenue}
openEditEvent = openEditEvent
/>
<EventCardBody
openEventDetail={() => this.props.openEventDetail()}
imageDownloadUrl={this.props.imageDownloadUrl}
/>
<EventCardFooter
openEventDetail={() => this.props.openEventDetail()}
eventName={this.props.eventName}
eventStartDate={this.props.eventStartDate}
eventVenue={this.props.eventVenue}
eventAddressLine1={this.props.eventAddressLine1}
eventAddressLine2={this.props.eventAddressLine2}
/>
</Card>
);
}
I am new to react native.
My question is pretty simple: my screen contains 5 buttons. Each one opens the same < Modal > component. I need to dynamically change the content of the modal, depending on the button clicked.
For example:
if I click the first button, a text input will be shown into the modal.
If I click the second button, checkboxes will be shown into the modal.
Here's my modal :
<Modal
visible={this.state.modalVisible}
animationType={'slide'}
onRequestClose={() => this.closeModal()}>
<View style={style.modalContainer}>
<View style={style.innerContainer}>
<Text>This is content inside of modal component</Text>
<Button
onPress={() => this.closeModal()}
title="Close modal"
>
</Button>
</View>
</View>
</Modal>
Here I open it :
openModal() {
this.setState({ modalVisible: true });
}
Here I call the function (on button press) :
onPress={() => this.openModal()}
I've heard about using props/children, but I don't know how to use them is this case.
Can anyone please help ?
Here is quick example to show who to render different content based on input you provide.
Modal Content
renderModalContent(type, data) {
switch(type) {
1: {
return (
<View>{..data}</View>
)
}
2: {
return (
<Button>...</Button>
)
}
default: (<CustomComponent data={data} />)
}
}
Modal
<Modal>
<View>
{this.renderModalContent(this.state.type, this.state.modalContentData)}
</View>
</Modal>
Here you decide which view you want to render and pass its data.
openModal() {
this.setState({ modalVisible: true, type: 1, data: {...} });
}
You should modify your Modal component so that it renders the base layout with space for dynamic content to be rendered. The content will be passed in as children, via Props. This will mean the modal is dynamic and will / should support future requirements. Try to avoid the switch case in the modal render suggestion unless you have very specific requirements that are unlikely to change in the future, or if you want to do things the React way.
Then for each variant of your Modal (TextInput, Checkbox etc.) create a new Component that wraps the Modal component and have each button initiate rendering the specific component.
If you're using Redux then you would be creating containers, connecting to Redux and passing dynamic state variables. You don't have to use Redux but the principle is the same.
Here's a very basic example to illustrate my point.
// Basic modal that renders dynamic content
const Modal = props => {
const { children } = props;
render (
<View style={styles.modal} >
{children}
</View>
);
}
// Specific modal implementation with TextInput
const ModalWithTextInput = props => (
<Modal>
<TextInput
value={props.someValue}
/>
</Modal>
)
// Specific modal implementation with Switch
const ModalWithSwitch = props => (
<Modal>
<Switch
value={props.someValue}
/>
</Modal>
)
Then in your component that launches the modals, do something like this...
class MyComponent extends Component {
openTextModal = () => {
this.setState({ modalType: 'text' });
}
openSwitchModal = () => {
this.setState({ modalType: 'switch' });
}
renderModal = (type) => {
if (type === 'text') {
return(<ModalWithTextInput />)
}
if (type === 'switch') {
return(<ModalWithSwitch />)
}
}
render() {
const { modalType } = this.state;
render (
<View>
<View>
<TouchableWithX onPress={this.openTextModal} />
<TouchableWithX onPress={this.openSwitchModal} />
</View>
<View>
{this.renderModal(modalType)}
</View>
</View>
);
}
}
Please note this code has not tested but the principle is sound.