React Native 0.70多重复View组件导致无日志崩溃问题
React Native 0.70 无日志崩溃问题:添加3个及以上View组件触发崩溃
问题描述
开发React Native 0.70版本应用时,遇到以下异常:当页面中添加3个及以上如下结构的View组件时,应用直接崩溃,且控制台无任何错误日志输出;但添加2个及以下时,应用可正常运行。
触发崩溃的View组件代码:
<View className="w-full p-2 flex flex-row justify-between items-center mb-1 bg-white rounded"> <Text>Option A</Text> </View>
页面核心代码如下:
import { ImageBackground, SafeAreaView, ScrollView, Text, View, TextInput as Input, TouchableOpacity, Pressable } from 'react-native'; import React from 'react'; import { useState, useEffect } from 'react'; import { ROUTES } from '../../../constants'; import { useSelector } from 'react-redux'; import Icon from 'react-native-vector-icons/MaterialIcons'; import { FUNCTIONS } from '../../../helpers'; import { useNavigation } from '@react-navigation/native'; import { useMutation, useQuery } from '@apollo/client'; import { GET_ALL_QUESTION_BY_USER_ID_QUERY, GET_CONTEST_BY_CONTEST_ID_QUERY, UPDATE_CONTEST_MUTATION } from '../../../graphql/query'; import { UPDATE } from '../../../graphql/contestMutation'; import UpdateItem from '../../../components/Author/UpdateItem'; const EditContest = ({ route }) => { const [newData, setNewData] = useState({}); const [question, setQuestion] = useState([]); const [selected, setSelected] = useState([]); const [selectedOptions, setSelectedOptions] = useState([]); const [open, setOpen] = useState(false); const auth = useSelector(state => state.auth); const navigation = useNavigation(); const { contestId } = route.params; const options = ['Option 1', 'Option 2', 'Option 3', 'Option 4', 'Option 5']; const handleSelect = selectedOptions => { console.log('Selected options:', selectedOptions); }; const handleOptionPress = option => { const isSelected = selectedOptions.includes(option); if (isSelected) { setSelectedOptions(pre => pre.filter(item => item !== option)); } else { setSelectedOptions(pre => [...pre, option]); } }; const { loading, error, data, refetch } = useQuery(GET_CONTEST_BY_CONTEST_ID_QUERY, { variables: { id: contestId } }); const { loading: questionLoading, error: questionError, data: questionData } = useQuery(GET_ALL_QUESTION_BY_USER_ID_QUERY, { variables: { token: auth.token } }); const [updateNow, { loading: updateLoading, error: updateError, data: updateData }] = useMutation(UPDATE_CONTEST_MUTATION); const [update, { loading: updLoading, error: updError, data: updData }] = useMutation(UPDATE); const submitUpdate = () => { updateNow({ variables: { ...newData, questions: selected, id: contestId } }); }; useEffect(() => { if (data) { const optionsDefault = data.getContestByContestId.Questions.map(item => item.id); setSelected(optionsDefault); setNewData({ title: data.getContestByContestId.title, description: data.getContestByContestId.description, duration: data.getContestByContestId.duration, start: data.getContestByContestId.start, privacy: data.getContestByContestId.privacy, password: data.getContestByContestId.password }); } }, [data]); useEffect(() => { if (questionData) { const array = questionData.getAllQuestionByUserId.map(item => ({ key: item.id, value: item.title })); setQuestion(array); } }, [questionData]); if (!updateLoading && updateData) { console.log(updateData); FUNCTIONS.showToast2(updateData.updateContest.status, updateData.updateContest.message); navigation.navigate(ROUTES.AUTHOR_CONTEST); } else if (updateError) console.log(false, updateError.message); if (loading || error || !newData) { return ( <SafeAreaView> <ScrollView> <View className="h-screen px-2 py-1 w-screen bg-white"> <View className="w-full items-center justify-center border-b-4 border-green-500 bg-green-100 rounded p-2"> <Text className="font-bold text-xl ">Update Contest</Text> </View> </View> </ScrollView> </SafeAreaView> ); } else return ( <SafeAreaView> <View className="h-screen px-2 py-1 w-screen bg-white"> <View className="w-full items-center justify-center border-b-4 border-green-500 bg-green-100 rounded p-2"> <Text className="font-bold text-xl ">Update Contest</Text> </View> <ScrollView showsVerticalScrollIndicator={false}> <View className="rounded overflow-hidden my-1"> <ImageBackground source={{ uri: 'https://picsum.photos/900' }} className="w-full h-[140px]" /> <View className="w-full bg-[#2B3467] p-1"> <Text className="font-bold text-xl text-gray-50"> {data.getContestByContestId.title} </Text> </View> <View className="flex space-y-2"> {/* 这里添加3个及以上View组件就会崩溃 */} <View className="w-full p-2 flex flex-row justify-between items-center mb-1 bg-white rounded"> <Text>Option A</Text> </View> <View className="w-full p-2 flex flex-row justify-between items-center mb-1 bg-white rounded"> <Text>Option A</Text> </View> <View className="w-full p-2 flex flex-row justify-between items-center mb-1 bg-white rounded"> <Text>Option A</Text> </View> </View> </View> </ScrollView> <Pressable className="w-full bg-[#2B3467] p-2 my-2 rounded items-center" onPress={submitUpdate}> <Text className="font-bold text-white text-lg">Update</Text> </Pressable> </View> </SafeAreaView> ); }; export default EditContest;
问题原因分析
崩溃的核心原因是布局层级的高度冲突:
外层View设置了h-screen(强制占满屏幕高度),内部同时包含ScrollView和底部的Pressable组件。当ScrollView内的内容(3个及以上View)加上Pressable的总高度超过屏幕高度时,RN 0.70的Yoga布局引擎无法处理这种溢出情况,直接触发无日志崩溃。
解决方案
1. 移除外层View的固定高度限制
将外层View的h-screen类名移除,改用flex布局让容器自适应内容,同时给SafeAreaView添加flex:1确保页面充满屏幕:
// 修改前 <SafeAreaView> <View className="h-screen px-2 py-1 w-screen bg-white"> // 修改后 <SafeAreaView style={{ flex: 1 }}> <View className="flex-1 px-2 py-1 w-screen bg-white">
2. 调整ScrollView的布局层级
让ScrollView占据剩余可用空间,底部Pressable固定在页面底部,避免布局溢出:
修改后的完整布局部分:
<SafeAreaView style={{ flex: 1 }}> <View className="flex-1 px-2 py-1 w-screen bg-white"> <View className="w-full items-center justify-center border-b-4 border-green-500 bg-green-100 rounded p-2"> <Text className="font-bold text-xl ">Update Contest</Text> </View> <ScrollView showsVerticalScrollIndicator={false} style={{ flex: 1 }}> <View className="rounded overflow-hidden my-1"> <ImageBackground source={{ uri: 'https://picsum.photos/900' }} className="w-full h-[140px]" /> <View className="w-full bg-[#2B3467] p-1"> <Text className="font-bold text-xl text-gray-50"> {data.getContestByContestId.title} </Text> </View> <View className="flex space-y-2"> {/* 现在添加任意数量的View组件都不会崩溃 */} <View className="w-full p-2 flex flex-row justify-between items-center mb-1 bg-white rounded"> <Text>Option A</Text> </View> <View className="w-full p-2 flex flex-row justify-between items-center mb-1 bg-white rounded"> <Text>Option A</Text> </View> <View className="w-full p-2 flex flex-row justify-between items-center mb-1 bg-white rounded"> <Text>Option A</Text> </View> <View className="w-full p-2 flex flex-row justify-between items-center mb-1 bg-white rounded"> <Text>Option A</Text> </View> </View> </View> </ScrollView> <Pressable className="w-full bg-[#2B3467] p-2 my-2 rounded items-center" onPress={submitUpdate}> <Text className="font-bold text-white text-lg">Update</Text> </Pressable> </View> </SafeAreaView>
3. 避免第三方样式库的高度类名冲突
如果使用Tailwind CSS for React Native,h-screen可能和RN原生布局计算存在兼容性问题,建议改用RN原生StyleSheet定义布局:
import { StyleSheet } from 'react-native'; const styles = StyleSheet.create({ container: { flex: 1, width: '100%', paddingHorizontal: 8, paddingVertical: 4, backgroundColor: 'white' }, scrollContainer: { flex: 1 } }); // 使用时 <View style={styles.container}> <ScrollView style={styles.scrollContainer}> {/* 内容 */} </ScrollView> </View>
额外提示
RN 0.70作为Fabric架构的过渡版本,存在部分布局引擎兼容性问题。遇到无日志崩溃时,优先排查布局层级的高度/宽度冲突、组件嵌套过深等问题,这类情况通常是触发崩溃的核心原因。
内容的提问来源于stack exchange,提问作者Md. Masud Mazumder
相关产品推荐
相关产品推荐

