Как передать модальное состояние React Hook между компонентами? - PullRequest
0 голосов
/ 31 января 2020

Я создаю библиотеку компонентов, и я нахожусь в процессе обработки состояния между моим App.tsx (где компонент будет импортирован) и компонентом ContactActionSheet.tsx. Когда пользователь нажимает кнопку Показать модальное , должен появиться компонент (для отслеживания этого используется visible prop).

Однако мой компонент использует модальное значение из react-native-modal библиотека и отслеживает его состояние видимости с isVisible.

Моя проблема: Как сохранить общее состояние между этими двумя компонентами, чтобы я мог нажать кнопку Показать модальное , появится модальное окно. Затем, когда я что-то щелкаю по компоненту и вызывается функция 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;

1 Ответ

1 голос
/ 31 января 2020

Вы можете использовать функцию в качестве аргумента реагирующего компонента:

      <ContactActionSheet
        visible={visible}
        toggle={toggle} //your toggle setState function
        contactsList={contacts}
      />

Затем в вашем ContactActionSheet компоненте вы можете просто сделать props.toggle(), что вызовет функцию toggle на App, измените состояние на App и получите желаемое влияние на ваш модал (потому что visible является общим)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...