Home > Software design >  using setTimeout to render component in React Native
using setTimeout to render component in React Native

Time:02-15

I'm trying to render a component from my screen after 3 seconds, this is due to some time delay when the user changes accounts, I need those 3 seconds so the data can be exactly what the user wants. Before rendering that piece of component, I wanted to load a container with loading in the center. I have inserted the data into the component but I get an error from component exception: enter image description here

This is how I'm rendering the data:

export function Dashboard({ drawerAnimationStyle}: any) {
    const navigation: any = useNavigation();
    const { firebaseFunctions } = useAuth();

    const [isLoading, setIsLoading] = useState(true);
    const [transactions, setTransactions] = useState<DataListProps[]>([]);
    const [higlightData, setHighlightData] = useState<HighlightDataProps>({} as HighlightDataProps);
    const theme = useTheme();

    async function loadTransaction(): Promise<any> {
        const data = await firebaseFunctions.loadTransaction();

        setTransactions(data.transactionsFormatted);

        setHighlightData(data.higlightData);

        setIsLoading(false);

    }

    useEffect(() => {   
        loadTransaction();
    }, [transactions]);

    return (
        <Animated.View style={{ flex: 1, ...drawerAnimationStyle}}>         
            <Container>
                {
                    isLoading ? 
                    <LoadingContainer>
                        <ActivityIndicator color={theme.colors.blue} size='large'/>
                    </LoadingContainer>
                    :
                    <>
                        <Header>                            
                            <DrawerContainer onPress={() => navigation.openDrawer()}>
                                <Icon name="menu" />
                            </DrawerContainer>
                            <HighLightCards>
                                <HighLightCard type="up" title="Income" amount={higlightData.entries.amount} lastTransaction={higlightData.entries.lastTransaction}/>
                                <HighLightCard type="down"title="Outcome" amount={higlightData.outcome.amount}lastTransaction={higlightData.outcome.lastTransaction}/>
                                <HighLightCard type="total" title="Total" amount={higlightData.total.amount} lastTransaction={higlightData.total.lastTransaction}/>
                            </HighLightCards>
                        </Header>                       

                        {
                            setTimeout(() => {
                                return (
                                    <Transactions>
                                        <Title>Transactions</Title>
                                        {
                                            transactions.length <= 0 ?
                                            <NoTransaction>
                                                <LottieView 
                                                    source={NoTransactionLottie}
                                                    style={{
                                                        width: 100,
                                                        height: 100,                                                                    
                                                    }}
                                                    autoPlay
                                                    loop
                                                    resizeMode='contain'
                                                />
                                            </NoTransaction>
                                            :
                                            <TransactionsList
                                                data={transactions}                     
                                                keyExtractor={(item: { id: any }) => item.id}
                                                renderItem={({ item }: any) => <TransactionCard  data={item}/>}
                                            />
                                        }
                                    </Transactions>
                                );
                            }, 3000)
                        }
                    </>
                }
            </Container>
        </Animated.View>
    );
}

CodePudding user response:

setTimeout is not valid inside the function return because it doesn't have any implicit return. This is also an anti-pattern in that you will display after 3 seconds whether you have data or not. You should show data if you have it, and indicate loading (or failure) if you don't.

You already have a loading state in the indicator, so if you can't trust that, you should focus on fixing that instead of trying to delay rendering.

If you absolutely must delay rendering for some reason, don't use setTimeout in the return, use it in the function body. e.g.

...
const [delayComplete, setDelayComplete] = useState(false);
...

useEffect(() => {
  if (isLoading || delayComplete) return;

  setTimeout(() => {
    setDelayComplete(true)
  }, 3000);
}, [isLoading, delayComplete, setDelayComplete]);

// in return
  { delayComplete ? (...): (...) }

CodePudding user response:

It's not a good idea to introduce a race condition.

Instead, listen for your async data to be ready and set the loading flag appropriately.

useEffect(() => {   
  // Or however you decide to check if your state is set
  if (transactions.length && Object.keys(higlightData).length) setLoading(false);
}, [transactions, higlightData]);

// then

{loading ? <Loading /> : <StuffThatUsesTransactionsAndHighlightData />}

Also, if loading is not set in any other place, then this is derived state and should be simplified to

const loading = transactions.length && Object.keys(higlightData).length;
  • Related