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.
Related
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);
state = {
isGetOnceButtonClicked: false
}
doAddItems(){
this.state.isGetOnceButtonClicked= true;
}
buttonToggle(){
if(!this.state.isGetOnceButtonClicked){
return(
<Button
type = "clear"
title="Get Once"
onPress={() => this.doAddItems()}
/>
)
}else{
return(
<InputNumberSpinner></InputNumberSpinner>
)
}
}
render() {
return(
<ScrollView>
<View >
{ this.buttonToggle() }
</View>
</ScrollView>
}
I'm new to react-native. I want to show the component dynamically. So created the method in which return the button if condition satisfies else return created custom component but unfortunately it is showing error like "Text strings must be rendered within a component" If we return strings like "something" then it works fine but it throwing error whenever we are returning other than Text.
<ScrollView>
<View >
{ this.state.isGetOnceButtonClicked ? <InputNumberSpinner /> : <Button
type = "clear"
title="Get Once"
onPress={() => this.doAddItems()}
/> }
</View>
</ScrollView>
use a ternary operator for these kinds of tasks, if the value is true then it will show the spinner and if not, it will show the button
make sure to have a function that toggles the state of the button so buttonToggle will be like this:
buttonToggle(){
this.setState({isGetOnceButtonClicked: !this.state.isGetOnceButtonClicked});
}
I am using redux to track which screen user is at.
I have a button tab component:
Tab.js:
class Tab extends Component {
render() {
return (
<Button onPress={() => navigation.navigate(route)}>
<Icon
style={selected ? deviceStyle.tabSelectedColor : deviceStyle.tabDefaultColor}
type="Ionicons"
name={icon}
/>
<Text style={selected ? deviceStyle.tabSelectedColor : deviceStyle.tabDefaultColor}>
{title}
</Text>
</Button>
);
}
}
Then I call it from another component:
const items = [
{
screen: 'home',
title: 'home',
icon: 'apps',
route: 'Home'
}]
renderTab = () => {
return items.map((tabBarItem, index) => {
return (
<Tab
key={index}
title={tabBarItem.title}
icon={tabBarItem.icon}
route={tabBarItem.route}
/>
);
});
};
Now, when user change screen, how can I change the selected props in tab component?
There are two ways to achieve this.
1) Onclick / onpress of your Tab you can dispatch an action, which will update user's current screen, as currently, you are navigating user on that page.
2) On load of screen, which is going to load after tab click/press.
You just need an action which will update user's current screen
I am building my first app in React Native, I have a ListView that displays a number of items and each one is clickable. My question is when I click on a row I would like a modal to appear above. Similar in IOS to clicking and the adding a subview.
renderRow( rowData ){
return (
<TouchableHighlight onPress={() => this.pressRow()}>
<View style={styles.parcelInfoContainer}>
</View>
</TouchableHighlight>
);
}
Now the problem is I am unsure about what to do in the function called by pressRow in relation to rendering the new component. In IOS I would simply add the view at this point but I am not sure how to go about it in React Native. I understand the idea of components just not how to render a new component on the fly.
If you're going to render the same Modal, in terms of layout, you should define it in render() function. Rendering on the fly is not possible, but you can change the states to make modal show what you like.
onPressRow = (dataFromRow) => {
this.setState({
modalText: dataFromRow.text
})
this.showModal(); //Call your function to show modal
}
render() {
return (
<View>
<FlatList />
<Modal>
<View>
...
<Text>{this.state.modalText}</Text>
</View>
</Modal>
</View>
)
}
However, if you want different layout for each row, you should write a this.getModalContent() like function to determine which layout should be rendered. You should call this function in render => <Modal> {this.getModalContent()} </Modal>
getModalContent = () => {
switch (this.state.modalText) { // define a better logic here.
case 'a':
return ( <Text> hey </Text>)
...
}
}