首页 > 解决方案 > 获取异步数据后渲染

问题描述

我正在获取数据,useEffect()然后修改对象(根据应该呈现的图标修改 clients.unreadMessages),稍后将其发送到组件进行呈现。但是这个组件并不总是正确渲染,图标有时会丢失。我认为这是因为数据在渲染后被修改。

客户名单

  import Colors from '@helper/Colors';
import { useSelector } from '@redux/index';
import { HealthierLifeOption } from '@redux/types';
import React, { useEffect, useState } from 'react';
import { View, Text, FlatList, ActivityIndicator } from 'react-native';
import ClientItem from './ClientItem';
import {usePubNub} from "pubnub-react";

export type Client = {
    id: number;
    username: string;
    individualPlanPaid: boolean;
    healthierLifeOption?: HealthierLifeOption;
    company?: {
      id: number;
      companyName: string;
    };
    mentor?: {
      id: number;
      username: string;
    };
}

type Props = {
    navigation: any;
    clients: Client[];
    isAdmin?: boolean;
    isLoading: boolean;
    onEndReached: Function;
    fromAdminTab?: boolean
}

const ClientList = ({ navigation, clients, onEndReached, isLoading, isAdmin = false, fromAdminTab= false }: Props) => {
    const resultCount = useSelector(state => state.user.menteesResultCount);
    const [hasMoreResults, setHasMoreResults] = useState(true);
    const userId = useSelector(state => state.user.id);
    const pubnub = usePubNub();

    useEffect(() => {
      setHasMoreResults(resultCount !== clients?.length);
    }, [resultCount, clients]);

    useEffect(() => {
        getUnreadMessagesProccess().then(r => console.log("aaaaaaaaa"));
    }, []);



    async function setGrant() {
        return new Promise( async resolve => {
            await pubnub.grant({
                channels: [userId.toString() + '.*'],
                ttl: 55,
                read: true,
                write: true,
                update: true,
                get: true,
            }, response => resolve(response));
        });
    }

    async function getMetadata() {
        const options = {include: {customFields: true}};
        return await pubnub.objects.getAllChannelMetadata(options);
    }

    function setChannelsAndTokens(channelMetadata, channels, tokens) {
        channelMetadata.data.forEach((value, index) => {
            tokens.push(value.custom.lastToken);
            channels.push(value.id)
        });
    }

    async function getUnreadedMessages(channels, tokens) {
        return await pubnub.messageCounts({
            channels: channels,
            channelTimetokens: tokens,
        });
    }


    async function getUnreadMessagesProccess() {
        const tokens = ['1000'];
        const channels = ['1234567'];

        const auth = await setGrant();
        const channelMetadata = await getMetadata();

        const l = await setChannelsAndTokens(channelMetadata, channels, tokens);
        const unread = await getUnreadedMessages(channels, tokens).then((res) => {
            clients.forEach((value, index) => {
                if (res.channels[value.id + '-' + userId + '-chat']) {
                    value.unreadMessages = res["channels"][value.id + '-' + userId + '-chat'];
                } else {
                    value.unreadMessages = 0;
                }
            })
            console.log(res);
        });

        return unread;
    }

    return (
        <View>
            <FlatList
                keyExtractor={item => item.id.toString()}
                data={clients}
                onEndReached={() => hasMoreResults ? onEndReached() : null}
                onEndReachedThreshold={0.4}
                renderItem={item => (
                    <ClientItem
                        client={item.item}
                        isAdmin={isAdmin}
                        navigation={navigation}
                        fromAdminTab={fromAdminTab}
                    />
                )}
                ListFooterComponent={isLoading
                  ? () => (
                      <View style={{ padding: 20 }}>
                        <ActivityIndicator animating size="large" color={Colors.border_gray} />
                      </View>
                    )
                  : null
                }
            />
        </View>
    );
}
export default ClientList;

客户项目

import React, {useEffect, useState} from 'react';
import { Text, View, Image, TouchableOpacity } from 'react-native';
import Images from '@helper/Images';
import FontAwesome5 from 'react-native-vector-icons/FontAwesome5';
import Colors from '@helper/Colors';
import styles from '../styles';
import ClientModal from './ClientModal/ClientModal';
import { Client } from './ClientList';
import { HealthierLifeOption } from '@redux/types';

type Props = {
    client: Client;
    navigation: any;
    isAdmin?: boolean;
    fromAdminTab?: boolean
}

const ClientItem = ({ client, navigation, isAdmin = false, fromAdminTab= false }: Props) => {
    const [showModal, setShowModal] = useState(false);
    const [showIcon, setShowIcon] = useState(false)
    console.log(client.unreadMessages)

    useEffect(() =>{
        if(client.unreadMessages>0)
            setShowIcon(true);
    },[client.unreadMessages]);

    let clientIcon = Images.icon.logoIcon;

    const handleContinueButton = () => {
        if(!fromAdminTab) {
            navigation.navigate('ChatFromClientsList', { selectedId: client.id, showBackButton: true });
        }else {
            setShowModal(!showModal)
        }
    };

    return (
        <View style={styles.client_item_container}>
            <TouchableOpacity style={styles.client_content_container} onPress={handleContinueButton}
                >
                <View style={styles.image_container}>
                    <Image
                        style={styles.client_profile_img}
                        source={clientIcon}
                        resizeMode="contain"
                        resizeMethod="resize"
                    />
                </View>
                <View style={styles.title_container}>
                    <Text style={styles.title} >{ client.username } </Text>
                </View>
                <View style={styles.dot_container}>
                {showIcon  &&  <FontAwesome5
                    name="comment-dots"
                    size={20}
                    color={Colors.red}

                />  }
                </View>
                <View style={styles.hamburger_container} >
                    <FontAwesome5
                        name="bars"
                        size={30}
                        color={Colors.black}
                        onPress={() => setShowModal(!showModal)}
                    />
                </View>
                <View>
                    <ClientModal
                        isVisible={showModal}
                        onCollapse={(value: boolean) => setShowModal(value)}
                        closeModal={(value: boolean) => setShowModal(value)}
                        client={client}
                        navigation={navigation}
                        isAdmin={isAdmin}
                    />
                </View>
            </TouchableOpacity>
        </View>
    );
};
export default ClientItem;

此代码并不总是正确呈现:

{showIcon  &&  <FontAwesome5
                    name="comment-dots"
                    size={20}
                    color={Colors.red}

                />  }

标签: react-nativeasync-awaitrenderuse-effect

解决方案


您不应该计算 renderItem 中的值。

为什么你不计算 renderItem 之外的值传入

useEffect(() =>{
        if(client.unreadMessages>0)
            setShowIcon(true);
    },[client.unreadMessages]);

执行以下操作:

renderItem={item => (
                    <ClientItem
                        client={item.item}
                        isAdmin={isAdmin}
                        navigation={navigation}
                        fromAdminTab={fromAdminTab}
                        showIcon={item.item.unreadMessages>0}
                    />
                )}

顺便说一句,renderItem 不应该是一个匿名函数


推荐阅读