我正在构建一个组件库,并且正在处理App.tsx
(将导入组件的位置)和组件ContactActionSheet.tsx
之间的状态。当用户按下 Show Modal (显示模态)按钮时,该组件将出现(visible
属性用于跟踪该组件)。
但是,我的组件使用react-native-modal
库中的模式,并使用isVisible
跟踪其可见性状态。
我的问题: 如何保持这两个组件之间的共享状态,以便单击 Show Modal (显示模态)按钮,模态就会出现。然后,当我单击该组件上的某项并调用emailCall函数时,模式将关闭?我正在将其实现为库,因此,如果可能的话,请尝试在没有库的情况下执行此操作。
App.tsx
// Imports: Dependencies
import React, { useState, useEffect } from 'react';
import { Button, SafeAreaView } from 'react-native';
// Imports: Components
import ContactActionSheet from './src/ContactActionSheet';
// React Native App
const App = () => {
// React Hooks: State
const [ visible, toggle ] = useState(false);
// Open Action Sheet
const openActionSheet = () => {
try {
// React Hook: Toggle Modal
toggle((visible: boolean) => !visible);
}
catch (error) {
console.log(error);
}
};
// Contacts
const contacts = [
{
title: 'Company Headquarters',
type: 'Phone Number',
contact: '(555) 555-5555',
},
{
title: 'Retail Store',
type: 'Phone Number',
contact: '(777) 777-7777',
},
{
title: 'Company Headquarters',
type: 'Email',
contact: 'hq@company.com',
},
{
title: 'Retail Store',
type: 'Email',
contact: 'store@company.com',
},
];
return (
<SafeAreaView>
<Button
title="Show Modal"
onPress={() => openActionSheet()}
/>
<ContactActionSheet
visible={visible}
contactsList={contacts}
/>
</SafeAreaView>
)
};
// Exports
export default App;
ContactActionSheet.tsx
// Imports: Dependencies
import React, { useState } from 'react';
import { Button, Dimensions, StyleSheet, Text, View, Linking, TouchableOpacity } from 'react-native';
import Modal from 'react-native-modal';
import { ifIphoneX } from 'react-native-iphone-x-helper';
import Icon from 'react-native-vector-icons/Ionicons';
Icon.loadFont();
// Screen Dimensions
const { height, width } = Dimensions.get('window');
// TypeScript: Types
interface Contact {
title: string;
type: 'Email' | 'Phone Number' | string;
contact: string;
}
interface Props {
visible: any;
contactsList: Array<Contact>;
}
// Component: Contact Action Sheet
const ContactActionSheet = (props: Props) => {
const [ modalVisible, toggle ] = useState(true);
// Toggle Modal
const toggleModal = () => {
try {
// Toggle
toggle((modalVisible: boolean) => !modalVisible);
}
catch (error) {
console.log(error);
}
};
// Render Modal
const renderModal = () => {
try {
if (
props.visible === true
&& modalVisible === true
) {
return true;
}
else {
return false;
}
}
catch (error) {
console.log(error);
}
};
// Render Contact Selectors
const renderContactSelectors = (props: Props) => {
try {
if (props.contactsList.length >= 6) {
console.warn('Error: Maximum of 6 contacts allowed.');
}
else {
// Map Contacts List To Contact Selector
return props.contactsList.map((contact: Contact, index: number) => {
// Render Single Contact List
if (props.contactsList.length === 1) {
return (
<TouchableOpacity key={index} style={styles.contactSelectorSingle} onPress={() => callEmail(contact)}>
<Icon name={contact.type === 'Email' ? 'ios-mail': 'ios-call'} size={28} style={styles.icon} color="#323232"></Icon>
<View>
<Text style={styles.contactTitle}>{contact.title}</Text>
<Text style={styles.emailPhone} numberOfLines={1}>{contact.contact}</Text>
</View>
</TouchableOpacity>
);
};
// Render First Index
if (props.contactsList.indexOf(contact) === 0) {
return (
<TouchableOpacity key={index} style={styles.contactSelectorFirst} onPress={() => callEmail(contact)}>
<Icon name={contact.type === 'Email' ? 'ios-mail': 'ios-call'} size={28} style={styles.icon} color="#323232"></Icon>
<View>
<Text style={styles.contactTitle}>{contact.title}</Text>
<Text style={styles.emailPhone} numberOfLines={1}>{contact.contact}</Text>
</View>
</TouchableOpacity>
);
};
// Render Middle Indexes
if (
props.contactsList.indexOf(contact) >= 1
&& props.contactsList.indexOf(contact) !== props.contactsList.length - 1
&& props.contactsList.length >= 3
) {
return (
<TouchableOpacity key={index} style={styles.contactSelector} onPress={() => callEmail(contact)}>
<Icon name={contact.type === 'Email' ? 'ios-mail': 'ios-call'} size={28} style={styles.icon} color="#323232"></Icon>
<View>
<Text style={styles.contactTitle}>{contact.title}</Text>
<Text style={styles.emailPhone} numberOfLines={1}>{contact.contact}</Text>
</View>
</TouchableOpacity>
);
};
// Render Last Index
if (props.contactsList.indexOf(contact) === props.contactsList.length - 1) {
return (
<TouchableOpacity key={index} style={styles.contactSelectorLast} onPress={() => callEmail(contact)}>
<Icon name={contact.type === 'Email' ? 'ios-mail': 'ios-call'} size={28} style={styles.icon} color="#323232"></Icon>
<View>
<Text style={styles.contactTitle}>{contact.title}</Text>
<Text style={styles.emailPhone} numberOfLines={1}>{contact.contact}</Text>
</View>
</TouchableOpacity>
);
}
});
}
}
catch (error) {
console.log(error)
}
};
// Call/Email
const callEmail = (contact: Contact) => {
try {
// Check If Email
if (contact.type === 'Email') {
// Email Details
let email = `${contact.contact}`;
let subject = `${contact.title}`;
let body = '';
// Send Email
Linking.openURL(`mailto:${email}?subject=${subject}&body=${body}`);
}
// Check If Phone Number
else if (contact.type === 'Phone Number') {
// Call Phone Number
Linking.openURL(`tel:${contact.contact}`);
}
// Toggle Modal
toggleModal();
}
catch (error) {
console.log(error);
}
};
return (
<View style={styles.container}>
<Modal
isVisible={renderModal()}
style={styles.modal}
backdropOpacity={.30}
>
<View style={styles.modalContainer}>
<View style={styles.contactListContainer}>
{renderContactSelectors(props)}
</View>
<TouchableOpacity onPress={() => toggleModal()} style={styles.cancelButtonContainer}>
<Text style={styles.cancelText}>Cancel</Text>
</TouchableOpacity>
</View>
</Modal>
</View>
);
}
// Styles
const styles = StyleSheet.create({
container: {
width: width,
},
modal: {
margin: 0,
},
modalContainer: {
height: '100%',
alignItems: 'center',
justifyContent: 'flex-end',
},
contactListContainer: {
width: width - 20,
marginBottom: 10,
alignItems: 'center',
},
contactSelectorSingle: {
width: width - 20,
height: 65,
backgroundColor: '#FFFFFF',
borderRadius: 12,
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
},
contactSelectorFirst: {
width: width - 20,
height: 65,
backgroundColor: '#FFFFFF',
borderColor: '#7D7D7D',
borderBottomWidth: StyleSheet.hairlineWidth,
borderTopLeftRadius: 12,
borderTopRightRadius: 12,
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
},
contactSelector: {
width: width - 20,
height: 65,
backgroundColor: '#FFFFFF',
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
borderColor: '#7D7D7D',
borderBottomWidth: StyleSheet.hairlineWidth,
},
contactSelectorLast: {
width: width - 20,
height: 65,
backgroundColor: '#FFFFFF',
borderBottomLeftRadius: 12,
borderBottomRightRadius: 12,
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
},
icon: {
marginLeft: 25,
marginRight: 25,
},
contactTitle: {
fontFamily: 'System',
fontSize: 17,
fontWeight: '500',
marginBottom: 4,
color: '#323232',
width: width - 20 - 20 - 60,
},
emailPhone: {
fontFamily: 'System',
fontSize: 15,
fontWeight: '400',
color: '#7D7D7D',
width: width - 20 - 20 - 50,
},
cancelButtonContainer: {
alignItems: 'center',
justifyContent: 'center',
width: width - 20,
height: 60,
backgroundColor: '#FFFFFF',
...ifIphoneX({
marginBottom: 35,
},
{
marginBottom: 10,
}),
borderRadius: 12,
},
cancelText: {
fontFamily: 'System',
fontSize: 20,
color: '#007AFF',
fontWeight: '600',
},
actionSheetContainer: {
borderWidth: 2,
borderColor: 'green',
backgroundColor: 'red',
},
});
// Exports
export default ContactActionSheet;
答案 0 :(得分:2)
您可以将函数用作react component参数:
<ContactActionSheet
visible={visible}
toggle={toggle} //your toggle setState function
contactsList={contacts}
/>
然后,您可以在ContactActionSheet
组件中执行props.toggle()
,这将在toggle
上触发App
函数,更改App
的状态,并且对您的模式有理想的影响(因为visible
被共享)