如何在React Native(Expo CLI)中正确使用stopPropagation阻止弹窗关闭
React Native Modal筛选时自动关闭的解决办法
问题根源
你碰到的弹窗自动关闭,本质是事件冒泡搞的鬼——筛选组件的点击事件往上传播,触发了Modal相关的关闭逻辑。你之前加的onTouchEnd没生效,是因为React Native的onPress是合成事件,单独在onTouchEnd里阻止冒泡没用,而且放置的位置也不对。
具体解决步骤
1. 在筛选按钮的点击事件里阻止冒泡
直接在Filter子组件的<Text>的onPress里添加stopPropagation,从事件源头拦截冒泡:
修改Filter子组件的代码:
<Text onPress={(e) => { e.stopPropagation(); // 把事件拦在这里,不让它往上跑 props.primary ? dispatch(locationsAction.togglePrimaryFilter(item)) : dispatch(locationsAction.toggleSecondaryFilter(item)) }} style={isActive ? [styles.active, styles.textTag] : [styles.inactive, styles.textTag]} key={index} > {item} </Text>
2. 给Modal内容容器加一层事件拦截
如果上面的方法还不生效,说明事件可能从FlatList或外层View冒泡上去,那就用Pressable包裹Modal里的modalView,拦截所有内部点击事件,不让它们传到Modal背景区域:
修改父组件里的modalView部分:
<View style={styles.centeredView}> {/* 用Pressable把整个弹窗内容包起来,阻止事件冒泡 */} <Pressable style={styles.modalView} onPress={(e) => e.stopPropagation()} > {/* 原来的弹窗内容都放这里 */} <Text style={styles.modalText}>Hello World!</Text> <ScrollView style={styles.filterBoxContainer}> <Filter data={category} primary={true} text="Select category:" /> {/* ...其他Filter组件和内容 */} </ScrollView> {/* ...ActiveBox、关闭按钮等 */} </Pressable> </View>
3. 排查状态更新导致的意外重渲染
如果还是有问题,检查Redux状态更新会不会间接重置modalVisible——比如filteredLocations计算导致组件重渲染时,modalVisible被意外改变。确保只有点击「Hide Modal」按钮和onRequestClose时才调用setModalVisible。
完整修改后的代码示例
Filter子组件
<View style={styles.container}> <Text style={styles.title}>{props.text}</Text> <FlatList horizontal showsHorizontalScrollIndicator={false} data={data} initialScrollIndex={0} getItemLayout={(data, index) => ({length: 100, offset: 40 * index, index})} keyExtractor={item => item} renderItem={({ item, index }) => { let isActive = allFilters.includes(item) return ( <View style={ isActive ? [styles.option, styles.active, { width: props.large ? 100 : 100 }] : [styles.option, styles.inactive, { width: props.large ? 100 : 100 }] }> <Text onPress={(e) => { e.stopPropagation(); props.primary ? dispatch(locationsAction.togglePrimaryFilter(item)) : dispatch(locationsAction.toggleSecondaryFilter(item)) }} style={isActive ? [styles.active, styles.textTag] : [styles.inactive, styles.textTag]} key={index} > {item} </Text> </View> ) }} /> </View>
父组件Modal部分
<Modal animationType="slide" transparent={true} visible={modalVisible} onRequestClose={() => { Alert.alert('Modal has been closed.'); setModalVisible(!modalVisible); }} > <View style={styles.centeredView}> <Pressable style={styles.modalView} onPress={(e) => e.stopPropagation()} > <Text style={styles.modalText}>Hello World!</Text> <ScrollView style={styles.filterBoxContainer}> <Filter data={category} primary={true} text="Select category:" /> <Filter data={cuisine} text="Select cuisine:" /> <Filter data={tags} large={true} text="Select your preferences:" /> <View style={styles.moreFilters}> <Text style={styles.text} onPress={toggleMoreFilters}> Use {moreFilters ? "less" : "more"} filters{" "} <AntDesign name={moreFilters ? "up" : "down"} size={16} color="black" /> </Text> </View> {moreFilters ? ( <> <Filter data={mealType} text="Select meal time:" /> <Filter data={district} large={true} text="Select district:" /> </> ) : ( <Text></Text> )} </ScrollView> {activeFilter.primary !== "" || activeFilter.secondary.length !== 0 ? ( <> <ActiveBox data={activeFilter} /> <Text style={[styles.text, styles.found]}> {filteredLocations.length === 1 ? '1 location' : `${filteredLocations.length} locations` } found matching your {isPremium ? '' : 'FREE '}search </Text> </> ) : ( <Text></Text> )} <Pressable style={[styles.button, styles.buttonClose]} onPress={() => setModalVisible(!modalVisible)} > <Text style={styles.textStyle}>Hide Modal</Text> </Pressable> </Pressable> </View> </Modal>
为啥之前的onTouchEnd没用?
React Native里的onPress是把onTouchStart、onTouchEnd等触摸事件封装起来的合成事件,单独给onTouchEnd加阻止冒泡,根本拦不住onPress的事件流,所以直接在onPress里处理才靠谱。
内容的提问来源于stack exchange,提问作者TkAle
相关产品推荐
相关产品推荐

