user2994290
user2994290

Reputation: 329

Render after fetching async data

I am fetching data in useEffect() and then modifying the object (modifying clients.unreadMessages based on what should render icon), which is later sent to the component to render. But this component does not render correctly always, the icon is sometimes missing. I think it's because data are modified after rendering.

ClientList

  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;

ClientItem

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;

This code does not render correctly always:

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

                />  }

Upvotes: 0

Views: 136

Answers (1)

Michael Bahl
Michael Bahl

Reputation: 3649

You should not calculate the value in renderItem.

Why you don’t calc the value outside of renderItem in pass it in

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

Do something like:

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

By the way renderItem should not be a anonymous function.

Upvotes: 1

Related Questions