【问题标题】:SetState causes many rerendersSetState 导致许多重新渲染
【发布时间】:2020-04-06 13:18:26
【问题描述】:

我正在创建一个信使应用程序,并且我有一个用于所有消息主屏幕的组件。我试图确保每次用户发送或接收消息时,组件都会使用最后发送的消息呈现和更新Flatlist。我有一个 firebase 实时数据库的监听器来监听更新。 我在我的应用程序中使用了函数式组件,我认为我错误地使用了 setState,因为它会导致我的组件多次重新渲染。 我已经彻底检查了提出的问题,但似乎没有一个与我的问题有关。 这是我的代码。


const AllMessagesScreen = ({ navigation }) => {
    const uid = firebase.auth().currentUser.uid;

    const [state, setState] = useState({
        allMessages: []
    });

    const { allMessages } = state;

    const fsdb = firebase.firestore().collection('Users');
    const db = database().ref(`messages/${uid}`); //realtime database

    const get = async () => {

        try {
            await db.on('value', snapshot => {

                snapshot.forEach(item => {

                    const { key: _id } = item;
                    const { lastMessage } = item.val();

                    fsdb.doc(_id).get().then((doc => {
                        const otherUser = doc.data();
                        const newChat = {
                            id: _id,
                            name: otherUser.name,
                            picture: otherUser.pictures.thumbnailUrl,
                            lastMessage: lastMessage
                        };
                        setState(prevState => ({
                             allMessages: [...prevState.allMessages, newChat]
                        }));
                    })).catch((err) => {
                        console.log('caught', err)
                    });
                });
            });
        } catch (err) {
            console.log('error caught', err)
        };

    };

    useEffect(() => {
        get();

    }, []);

    return (
        <View style={styles.screen}>
            <FlatList
                data={allMessages}
                keyExtractor={item => item.id}
                renderItem={({ item }) => (
                    <View style={{ flex: 1 }}>
                        <MessageCard
                            id={item.id}
                            name={item.name}
                            image={item.picture}
                            lastMessageText={item.lastMessage.text}
                        />
                    </View>
                )}
            />

        </View>
    );

}

我也试过这样设置状态

setState({ allMessages: [...allMessages, newUser] });
``
The second way only shows the last rendered message and gets rid of the rest.
Any help is really appreciated

【问题讨论】:

    标签: javascript reactjs react-native


    【解决方案1】:

    你不应该在循环中更新状态,从循环内部取出setState并在循环之后调用它。

    在每个state 更新Reactre-render component,在这种情况下Reactre-render component 在每次迭代后,这可能会导致意外的结果。

    您还有其他几个问题,我想强调一下。

    1. 你不应该使用像const { allMessages } = state; 这样的状态,它总是会在每次渲染时给你一个空数组。在需要时像state.allMessages 一样使用它。

    2. 在每个渲染中,您都获得了可能导致内存泄漏的database 连接,请使用useRef 来保持db 连接。

    3. 初始化useEffect钩子内的数据库连接和其他东西(在组件安装上)。

    我已更新将解决上述问题的代码(我已将 then 块更改为等待,因为您是代码中的 async/await 并将 forEach 替换为 for of)。

    import React, { useState, useRef } from 'react';
    const AllMessagesScreen = ({ navigation }) => {
        const [state, setState] = useState({
            allMessages: []
        });
        let fsdb = useRef();
        let db = useRef();
        useEffect(() => {
            const uid = firebase.auth().currentUser.uid;
            fsdb.current = firebase.firestore().collection('Users');
            db.current = database().ref(`messages/${uid}`); //realtime database
            fetchMessage();
        }, []);
        async function fetchMessage() {
            try {
                await db.current.on('value', snapshot => {
                    let newChat = []
                    for (const item of snapshot) {
                        const { key: _id } = item;
                        const { lastMessage } = item.val();
                        let doc = await fsdb.current.doc(_id).get();
                        const otherUser = doc.data();
                        newChat.push({
                            id: _id,
                            name: otherUser.name,
                            picture: otherUser.pictures.thumbnailUrl,
                            lastMessage: lastMessage
                        });
                    }
                    setState(prevState => ({
                        allMessages: [...prevState.allMessages, ...newChat]
                    }));
                });
            } catch (err) {
                console.log('error caught', err)
            };
        };
    
        return (
            <View style={styles.screen}>
                <FlatList
                    data={state.allMessages}
                    keyExtractor={item => item.id}
                    renderItem={({ item }) => (
                        <View style={{ flex: 1 }}>
                            <MessageCard
                                id={item.id}
                                name={item.name}
                                image={item.picture}
                                lastMessageText={item.lastMessage.text}
                            />
                        </View>
                    )}
                />
            </View>
        );
    }
    

    【讨论】:

    • 谢谢索海尔。我已经尝试过了,但由于某种原因 allMessages 永远不会更新,并且始终是一个空数组。你确定这是设置状态的正确方法吗?
    • 谢谢@Sohail。我已经尝试过了,现在出现错误:TypeError: undefined is not a function (near '...tor" : "@@iterator"]();...')
    • 很抱歉让您感到困惑,这是一个警告,但我的 FlatList 没有呈现。也没有行号
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-19
    • 2019-03-12
    • 1970-01-01
    • 1970-01-01
    • 2016-09-09
    • 2020-11-08
    • 2019-09-26
    相关资源
    最近更新 更多