由不同组件打开的 React Native 关闭 Modal

Posted

技术标签:

【中文标题】由不同组件打开的 React Native 关闭 Modal【英文标题】:React Native close Modal that is opened by different component 【发布时间】:2021-03-23 05:08:22 【问题描述】:

您好,我是 React Native 的新手,目前我的模态组件有问题。我的模态组件有两个道具,gameData 和 isModalVisible。在 Home.js 中,模态属性 isModalVisible 具有状态变量 isVisible 的值,当按下某个 TouchableOpacity 时,该值变为 true。然后在我的 FeaturedGameModal.js 中 isModalVisible 是从道具设置的。我遇到的问题是关闭模式。以这种方式打开模态很好,但是我应该如何关闭模态,因为它的可见性是由 Home.js 中的道具控制的?任何帮助将不胜感激。我已经为此工作了两天,这让我发疯。谢谢!如果您想更仔细地检查我的代码,我将包含我的两个文件。

Home.js:

import React from 'react';
import 
  View,
  Text,
  Image,
  SafeAreaView,
  TouchableOpacity,
  ActivityIndicator,
  Linking,
  ScrollView,
  TouchableHighlight,
 from 'react-native';
import homeStyles from '../styles/homeStyles';
import styles from '../styles/styles';
import createIconSetFromIcoMoon from 'react-native-vector-icons';
import icoMoonConfig from '../../assets/fonts/selection.json';
import fetchData from '../functions/fetch';
import Modalz from '../modals/FeaturedGameModal';

const Icon = createIconSetFromIcoMoon(icoMoonConfig);

class Home extends React.Component 
  myData = ;

  constructor(props) 
    super(props);
    this.state = 
      error: false,
      isFetching: true,
      featuredGameModal: false,
      isVisible: false,
    ;
  

  handleFeaturedGame = async () => 
    this.setState(, async () => 
      try 
        const featureGameData = await fetchData(
          'http://dev.liberty.edu/templates/flames/json/json_appHomeFeed.cfm',
        );

        this.setState(
          error: false,
          featuredGameData: featureGameData,
          isFetching: false,
        );
       catch (e) 
        this.setState(
          error: true,
        );
        console.log(e.message);
      
    );
  ;

  handleFeaturedModal() 
    this.setState(featuredGameModal: false);
  

  componentDidMount() 
    this.handleFeaturedGame();
  

  render() 
    const featuredGameData = this.state;
    return this.state.isFetching ? (
      <View style=styles.center>
        <ActivityIndicator size="large" color="#AE0023" />
      </View>
    ) : (
      <ScrollView>
        <SafeAreaView>
          <View style=homeStyles.featuredGameContainer>
            <View style=homeStyles.centerHor>
              <Image
                style=homeStyles.logo
                source=require('../../assets/images/FlamesLogo.png')
              />
            </View>
            <View style=homeStyles.gameTimeContainer>
              <Text style=homeStyles.gameTime>
                featuredGameData.featuredGame.eventdate
              </Text>
              <Text style=homeStyles.gameTime>
                featuredGameData.featuredGame.eventtime
              </Text>
            </View>
            <TouchableOpacity
              activeOpacity=0.6
              onPress=() => 
                this.setState(isVisible: true);
              >
              <View style=homeStyles.contentContainer>
                <View style=homeStyles.contentLeft>
                  <Text style=homeStyles.teamText>
                    featuredGameData.featuredGame.teamname
                  </Text>
                  <Text style=homeStyles.opponentText>
                    vs featuredGameData.featuredGame.opponent
                  </Text>
                  <Text style=homeStyles.locationText>
                    <Icon size=12 name='location' />
                    &nbsp;featuredGameData.featuredGame.location
                  </Text>
                </View>
                <View style=homeStyles.contentRight>
                  <Image
                    style=homeStyles.opponentLogo
                    source=
                      uri: featuredGameData.featuredGame.OpponentLogoFilename,
                    
                  />
                </View>
              </View>
            </TouchableOpacity>
            <View style=homeStyles.allContent>
              <Modalz
                gameData=this.state.featuredGameData.featuredGame
                isModalVisible=this.state.isVisible
              />
              <View style=homeStyles.contentContainerBottom>
                <View style=homeStyles.contentLeft>
                  <TouchableOpacity
                    style=homeStyles.buyTicketBtn
                    onPress=() =>
                      Linking.openURL(featuredGameData.featuredGame.buyTickets)
                    >
                    <Text style=homeStyles.buyTicketBtnText>Buy Tickets</Text>
                  </TouchableOpacity>
                </View>
                <View style=homeStyles.liveContainer>
                  <Text style=homeStyles.live>Experience Live:</Text>
                  <View style=homeStyles.liveIconsContainer>
                    <Icon
                      style=color: '#FFF', marginRight: 4
                      size=15
                      name='radio'
                    />
                    <Icon style=color: '#FFF' size=12 name='LFSN' />
                  </View>
                </View>
              </View>
            </View>
          </View>
          <View style=homeStyles.newsContainer>
            featuredGameData.News.map((item, key) => (
              <View
                key=key
                style=[homeStyles.centerHor, homeStyles.newsCard]>
                <Image
                  style=homeStyles.newsImage
                  source=
                    uri: item.Thumbnail,
                  
                />
                <Text style=homeStyles.headline>item.Headline</Text>
                <View style=homeStyles.teamNameView>
                  <Text style=homeStyles.teamNameText>item.teamname</Text>
                  <Text>item.GameDate</Text>
                </View>
              </View>
            ))
          </View>
        </SafeAreaView>
      </ScrollView>
    );
  


export default Home;

FeaturedGameModal.js:

import React from 'react';
import 
  Alert,
  Modal,
  StyleSheet,
  Text,
  TouchableHighlight,
  View,
  Image,
  Dimensions,
  TouchableOpacity,
  SafeAreaView,
 from 'react-native';

import createIconSetFromIcoMoon from 'react-native-vector-icons';
import icoMoonConfig from '../../assets/fonts/selection';
import homeStyles from '../styles/homeStyles';

const Icon = createIconSetFromIcoMoon(icoMoonConfig);

const windowWidth = Dimensions.get('window').width;

export default class Modalz extends React.Component 
  constructor(props) 
    super(props);
    this.state = 
      teamName: props.gameData.teamname,
      opponentName: props.gameData.opponent,
      eventDate: props.gameData.eventdate,
      liveAudioURL: props.gameData.LiveAudioURL,
      liveStatsURL: props.gameData.LiveStatsURL,
      videoURL: props.gameData.VideoURL,
      opponentLogoURL: props.gameData.OpponentLogoFilename,
    ;
  

  render() 
    const 
      opponentName,
      teamName,
      eventDate,
      opponentLogoURL,
      liveStatsURL,
      liveAudioURL,
      videoURL,
      location,
     = this.state;

    const isModalVisible = this.props;

    return (
      <View>
        <View style=styles.centeredView>
          <Modal
            animationType="slide"
            transparent=true
            visible=isModalVisible
            onRequestClose=() => 
              Alert.alert('Modal has been closed.');
            >
            <SafeAreaView style=flex: 1, backgroundColor: 'transparent'>
              <View style=styles.centeredView>
                <View style=styles.modalView>
                  <Icon
                    style=styles.closeButton
                    size=25
                    name='x'
                    onPress=() => 
                  />

                  <Text style=styles.upcomingGameTitle>
                    teamName vs opponentName
                  </Text>
                  <Text style=styles.upcomingGameSubtitle>eventDate</Text>
                  <View style=styles.facingLogosBlock>
                    <View style=styles.leftTeamBlock />
                    <View style=styles.rightTeamBlock />
                    <View style=styles.vsTextWrapper>
                      <Text style=styles.vsText>VS</Text>
                    </View>
                    <View style=styles.logoWrapper>
                      <Image
                        style=styles.facingLogoImg
                        source=
                          uri:
                            'https://www.liberty.edu/templates/flames/images/flamesMonogram.png',
                        
                      />
                      <Image
                        style=styles.facingLogoImg
                        source=uri: opponentLogoURL
                      />
                    </View>
                  </View>

                  <View>
                    <TouchableOpacity style=styles.buyTicketBtn>
                      <Text style=styles.buyTicketBtnText>Buy Tickets</Text>
                    </TouchableOpacity>
                  </View>
                  <View style=styles.buttonRow>
                    <TouchableOpacity
                      style=...styles.iconButton, ...styles.iconButtonLeft>
                      <Icon
                        style=styles.iconButtonIcon
                        size=25
                        name='flag'
                        onPress=() => 
                          this.toggleModal(!this.state.modalVisible);
                        
                      />
                      <Text style=styles.iconButtonText>Game Day</Text>
                    </TouchableOpacity>

                    <TouchableOpacity
                      style=...styles.iconButton, ...styles.iconButtonRight>
                      <Icon
                        style=styles.iconButtonIcon
                        size=25
                        name='stats'
                        onPress=() => 
                          this.toggleModal(!this.state.modalVisible);
                        
                      />
                      <Text style=styles.iconButtonText>Live Stats</Text>
                    </TouchableOpacity>
                  </View>
                  <View style=styles.liveLinkBlock>
                    <View style=styles.liveLinkLeft>
                      <Icon
                        style=styles.iconButtonIcon
                        size=18
                        name='LFSN'
                      />
                      <Text>The Journey 88.3 FM</Text>
                    </View>
                    <TouchableOpacity style=styles.liveButton>
                      <Text style=styles.liveButtonText>Listen Live</Text>
                    </TouchableOpacity>
                  </View>
                  <View style=styles.liveLinkBlock>
                    <View style=styles.liveLinkLeft>
                      <Icon
                        style=styles.iconButtonIcon
                        size=18
                        name='espn3'
                      />
                      <Text>LFSN TV Production</Text>
                    </View>
                    <TouchableOpacity style=styles.liveButton>
                      <Text style=styles.liveButtonText>Watch Live</Text>
                    </TouchableOpacity>
                  </View>
                </View>
              </View>
            </SafeAreaView>
          </Modal>
        </View>
      </View>
    );
  


const styles = StyleSheet.create(
  centeredView: 
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  ,
  modalView: 
    flex: 1,
    alignSelf: 'stretch',
    backgroundColor: 'white',
    borderTopLeftRadius: 20,
    borderTopRightRadius: 20,
    paddingTop: 14,
    alignItems: 'center',
    shadowColor: '#000',
    shadowOffset: 
      width: 0,
      height: 2,
    ,
    shadowOpacity: 0.25,
    shadowRadius: 3.84,
    elevation: 5,
  ,
  openButton: 
    backgroundColor: '#F194FF',
    borderRadius: 20,
    padding: 10,
    elevation: 2,
  ,
  closeButton: 
    position: 'absolute',
    right: 16,
    top: 16,
    color: '#000',
  ,
  closeText: 
    color: '#000',
    fontWeight: 'bold',
    textAlign: 'center',
    fontSize: 20,
  ,
  upcomingGameTitle: 
    color: '#19191A',
    fontSize: 18,
    fontWeight: 'bold',
  ,
  upcomingGameSubtitle: 
    color: '#747676',
    fontSize: 13,
    fontWeight: 'bold',
    marginBottom: 16,
  ,
  modalText: 
    marginBottom: 15,
    textAlign: 'center',
  ,
  facingLogosBlock: 
    flexDirection: 'row',
    position: 'relative',
    alignItems: 'center',
  ,
  facingLogoImg: 
    width: 100,
    height: 100,
    resizeMode: 'contain',
    flex: 1,
  ,
  leftTeamBlock: 
    width: 0,
    height: 0,
    backgroundColor: 'transparent',
    borderStyle: 'solid',
    borderRightWidth: 35,
    borderTopWidth: 185,
    borderRightColor: 'transparent',
    borderTopColor: '#AE0023',
    borderLeftColor: '#AE0023',
    borderLeftWidth: windowWidth / 2,
    left: 15,
    zIndex: -1,
    position: 'relative',
  ,
  rightTeamBlock: 
    width: 0,
    height: 0,
    backgroundColor: 'transparent',
    borderStyle: 'solid',
    borderLeftWidth: 35,
    borderBottomWidth: 185,
    borderBottomColor: '#461964',
    borderRightColor: '#461964',
    borderLeftColor: 'transparent',
    borderRightWidth: windowWidth / 2,
    right: 15,
    zIndex: -1,
    position: 'relative',
  ,
  vsTextWrapper: 
    position: 'absolute',
    top: 0,
    left: 0,
    right: 0,
    bottom: 0,
    justifyContent: 'center',
    alignItems: 'center',
  ,
  vsText: 
    color: '#000000',
    backgroundColor: '#FFFFFF',
    padding: 5,
    fontWeight: 'bold',
  ,
  logoWrapper: 
    position: 'absolute',
    width: windowWidth,
    height: 185,
    top: 0,
    left: 35,
    flexDirection: 'row',
    alignItems: 'center',
  ,
  buyTicketBtn: 
    marginTop: 24,
    backgroundColor: '#AE0023',
    borderRadius: 4,
    paddingVertical: 20,
    paddingHorizontal: 12,
    width: windowWidth - 24,
  ,
  buyTicketBtnText: 
    fontSize: 21,
    color: '#fff',
    fontWeight: 'bold',
    alignSelf: 'center',
    textTransform: 'uppercase',
  ,
  buttonRow: 
    paddingVertical: 24,
    paddingHorizontal: 12,
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
  ,
  iconButton: 
    backgroundColor: '#F0F3F5',
    borderRadius: 4,
    paddingVertical: 14,
    alignItems: 'center',
    justifyContent: 'center',
    flexDirection: 'row',
    flex: 1,
  ,
  iconButtonText: 
    color: '#19191A',
    fontWeight: 'bold',
    fontSize: 16,
    marginLeft: 10,
  ,
  iconButtonIcon: 
    color: '#000',
  ,
  iconButtonLeft: 
    marginRight: 6,
  ,
  iconButtonRight: 
    marginLeft: 6,
  ,
  liveLinkBlock: 
    padding: 12,
    borderStyle: 'solid',
    borderTopColor: '#F0F3F5',
    borderTopWidth: 1,
    alignItems: 'center',
    justifyContent: 'center',
    flexDirection: 'row',
  ,
  liveButton: 
    backgroundColor: '#F0F3F5',
    borderRadius: 4,
    paddingVertical: 14,
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  ,
  liveButtonText: 
    color: '#19191A',
    fontWeight: 'bold',
    fontSize: 16,
  ,
  liveLinkLeft: 
    flex: 2,
  ,
);

【问题讨论】:

【参考方案1】:

您应该在 Home.js 中创建 hideModal 函数,然后将其传递给 Modalz 组件。

在 Home.js 中,添加这个函数:

hideModalz = () => 
    this.setState(isVisible: true);

并将这个函数传递给 Modalz 道具:

<Modalz
    gameData=this.state.featuredGameData.featuredGame
    isModalVisible=this.state.isVisible
    hide=hideModalz
/>

在 Modalz 中,如果您想隐藏 modal,请致电 this.props.hide();

【讨论】:

谢谢!我传递给模态的函数有点不同,但这让我朝着正确的方向前进。感谢您的帮助!

以上是关于由不同组件打开的 React Native 关闭 Modal的主要内容,如果未能解决你的问题,请参考以下文章

React-Native:将焦点设置到由数组构建的自定义组件

为啥我不能在 ios 设备上关闭并重新打开 react-native 调试?

React-Native 中的关闭重新加载页面

react native 无法运行 iOS

react中子组件的渲染与react native不同吗?

在 QRcodeScanner React native 中打开/关闭手电筒