You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 09:54:55