React Native FlatList加载更多时崩溃问题求助
React Native FlatList加载更多后崩溃问题
问题现象
初始加载时FlatList表现正常,触发加载更多更新状态后组件崩溃,尝试多种状态更新方式无效,未在谷歌找到同类问题,本人刚接触React Native,对其特性了解有限。
问题截图


原代码
// React import React, {useContext, useState, useEffect} from 'react'; import {View, Text, TouchableWithoutFeedback, FlatList} from "react-native"; import {MaterialCommunityIcons} from "@expo/vector-icons"; import * as Yup from "yup"; import {ray} from 'node-ray/web'; import AuthContext from "../../../auth/context"; import AppContainer from "../../../components/AppContainer"; import ChatContext from "../../../context/chats-context"; import ChatsApi from "../../../api/chats"; import Form from "../../../components/form/Form"; import FormInputDefault from "../../../components/form/FormInputDefault"; import FormIconButton from "../../../components/form/FormIconButton"; import RenderChatMessageItem from "../../../components/chat/RenderChatMessageBox"; import tw from "../../../lib/tailwind"; const validationSchema = Yup.object().shape({ message: Yup.string().required().label("Message"), }); function SharedMessagesShow({ navigation, route }) { const chatContext = useContext(ChatContext); const { user } = useContext(AuthContext); const { data } = route.params; const [ chat, setChat ] = useState(JSON.parse(data)); const [ currentPage, setCurrentPage ] = useState(1); const [ maxListItems, setMaxListItems ] = useState(10); const [ loading, setLoading ] = useState(false); const loadOlderMessages = async () => { if (loading) { return } setLoading(true); const response = await ChatsApi.getMoreMessages(chat.id, user.user.id, currentPage + 1); if (!response.ok) { return null; } if (maxListItems < response.data.total) { const newChat = {...chat}; let newPage = currentPage + 1; let newMaxItems = maxListItems + response.data.messages.length; setCurrentPage(newPage); setMaxListItems(newMaxItems); newChat.messages = newChat.messages.concat(response.data.messages); ray(newChat); setChat(newChat); setLoading(false); } } const handleSendMessage = async ({message}, resetForm) => { const toUser = chat.users.find(chatUser => { return chatUser.user_id !== user.user.id }) const response = await ChatsApi.sendMessage(chat.id, message, user.user.id, toUser.id); if (!response.ok) { return null; } const newChat = {...chat}; newChat.messages.shift(); newChat.messages.push(response.data); setChat(newChat); resetForm(); } useEffect(() => { }, [chat, chat.messages, maxListItems]); return ( <AppContainer bgColour={'gray-100'}> <View style={tw`justify-between flex flex-1`}> <View style={tw`bg-white p-3 flex flex-row justify-between items-center`}> <View style={tw`flex`}> <Text>{chat.to_user.first_name} {chat.to_user.last_name}</Text> <Text>{chat.application.job_name}</Text> <Text>{chat.application.status}</Text> </View> <View style={tw`flex mr-3`}> <TouchableWithoutFeedback onPress={() => navigation.navigate('Chat Calendar')}> <MaterialCommunityIcons name={'calendar-clock'} size={25} color={'#000000'}/> </TouchableWithoutFeedback> </View> </View> <View style={tw`flex flex-1 p-3`}> <FlatList inverted={true} data={chat.messages} onEndReachedThreshold={0.1} renderItem={ message => <RenderChatMessageItem message={message} fromUser={user.user}/> } keyExtractor={ message => message.id.toString()} onEndReached={loadOlderMessages} extraData={maxListItems} initialNumToRender={maxListItems} /> </View> <View style={tw`flex w-full`}> <Form initialValues={{ message: "" }} onSubmit={ (values, {resetForm}) => handleSendMessage(values, resetForm) } validationSchema={validationSchema} > <View style={tw`flex flex-row w-full p-1`}> <FormInputDefault name="message" placeholder="Message" textContentType="none" autoCapitalize="sentences" autoCorrect={true} multiline={true} textAlignVertical={true} scrollEnabled={true} styles='w-5/6 bg-white flex px-8 pt-3 pb-1 rounded-3xl max-h-20' /> <View style={tw`w-1/6 justify-center items-center`}> <FormIconButton defaultText='+' /> </View> </View> </Form> </View> </View> </AppContainer> ); } export default SharedMessagesShow;
修复后的代码
// React import React, {useContext, useState, useEffect} from 'react'; import {View, Text, TouchableWithoutFeedback, FlatList} from "react-native"; import {MaterialCommunityIcons} from "@expo/vector-icons"; import * as Yup from "yup"; import {ray} from 'node-ray/web'; import AuthContext from "../../../auth/context"; import AppContainer from "../../../components/AppContainer"; import ChatContext from "../../../context/chats-context"; import ChatsApi from "../../../api/chats"; import Form from "../../../components/form/Form"; import FormInputDefault from "../../../components/form/FormInputDefault"; import FormIconButton from "../../../components/form/FormIconButton"; import RenderChatMessageItem from "../../../components/chat/RenderChatMessageBox"; import tw from "../../../lib/tailwind"; const validationSchema = Yup.object().shape({ message: Yup.string().required().label("Message"), }); function SharedMessagesShow({ navigation, route }) { const chatContext = useContext(ChatContext); const { user } = useContext(AuthContext); const { data } = route.params; const [ chat, setChat ] = useState(JSON.parse(data)); const [ currentPage, setCurrentPage ] = useState(1); const [ maxListItems, setMaxListItems ] = useState(10); const [ loading, setLoading ] = useState(false); const loadOlderMessages = async () => { if (loading) return; setLoading(true); const response = await ChatsApi.getMoreMessages(chat.id, user.user.id, currentPage + 1); if (!response.ok) { setLoading(false); return null; } if (maxListItems < response.data.total) { setCurrentPage(prev => prev + 1); setMaxListItems(prev => prev + response.data.messages.length); setChat(prevChat => { const newChat = {...prevChat}; newChat.messages = [...response.data.messages, ...prevChat.messages]; return newChat; }); } setLoading(false); } const handleSendMessage = async ({message}, resetForm) => { const toUser = chat.users.find(chatUser => chatUser.user_id !== user.user.id); const response = await ChatsApi.sendMessage(chat.id, message, user.user.id, toUser.id); if (!response.ok) return null; setChat(prevChat => { const newChat = {...prevChat}; newChat.messages = [...prevChat.messages.slice(1), response.data]; return newChat; }); resetForm(); } useEffect(() => {}, [chat, currentPage, maxListItems]); return ( <AppContainer bgColour={'gray-100'}> <View style={tw`justify-between flex flex-1`}> <View style={tw`bg-white p-3 flex flex-row justify-between items-center`}> <View style={tw`flex`}> <Text>{chat.to_user.first_name} {chat.to_user.last_name}</Text> <Text>{chat.application.job_name}</Text> <Text>{chat.application.status}</Text> </View> <View style={tw`flex mr-3`}> <TouchableWithoutFeedback onPress={() => navigation.navigate('Chat Calendar')}> <MaterialCommunityIcons name={'calendar-clock'} size={25} color={'#000000'}/> </TouchableWithoutFeedback> </View> </View> <View style={tw`flex flex-1 p-3`}> <FlatList inverted={true} data={chat.messages} onEndReachedThreshold={0.1} renderItem={ ({item: message}) => <RenderChatMessageItem message={message} fromUser={user.user}/> } keyExtractor={ message => message.id.toString()} onEndReached={loadOlderMessages} extraData={chat} initialNumToRender={10} /> </View> <View style={tw`flex w-full`}> <Form initialValues={{ message: "" }} onSubmit={ (values, {resetForm}) => handleSendMessage(values, resetForm) } validationSchema={validationSchema} > <View style={tw`flex flex-row w-full p-1`}> <FormInputDefault name="message" placeholder="Message" textContentType="none" autoCapitalize="sentences" autoCorrect={true} multiline={true} textAlignVertical="top" scrollEnabled={true} styles='w-5/6 bg-white flex px-8 pt-3 pb-1 rounded-3xl max-h-20' /> <View style={tw`w-1/6 justify-center items-center`}> <FormIconButton defaultText='+' /> </View> </View> </Form> </View> </View> </AppContainer> ); } export default SharedMessagesShow;
核心修复点
- RenderItem参数修正:FlatList的
renderItem回调参数是包含item的对象,原代码直接传递整个对象导致组件拿到错误数据,修复为解构item:({item: message}) => ...。 - 状态更新逻辑优化:
- 加载更多时使用函数式更新获取最新状态,避免异步操作引发的状态不一致。
- 更新
chat时通过函数式更新获取最新快照,同时深拷贝数组避免修改原state引用。
- FlatList配置调整:
extraData改为依赖chat,确保消息列表更新时FlatList能正确触发重渲染。initialNumToRender设为固定初始值,该属性仅控制首次渲染数量,无需动态修改。- 针对
inverted={true}的特性,将旧消息添加到数组开头,保证显示顺序符合聊天逻辑。
- 消息发送时的数组操作修正:用
slice(1)创建新数组替代shift(),避免直接修改原state数组。
内容的提问来源于stack exchange,提问作者James Hayes
相关产品推荐
相关产品推荐

