Использование Cloud Firestore с React Hooks приводит к массовому вызову Api при каждом отображении экрана - PullRequest
0 голосов
/ 16 января 2020

Я нахожусь в странной ситуации с использованием ловушек React и firebase, в первую очередь Привет, я работаю над нативным приложением React, использующим облачный firestore в качестве моего бэкэнд-сервиса и навигацию по React. Я обрабатываю выборку данных, как известно с помощью useEffect (() => {}, []), но я вызываю мой API в самом дочернем компоненте, поэтому я заметил, что каждый раз, когда мой код отображает его, запускается API-функция, вызывающая чрезвычайно больше операций чтения и пожарного хранилища. это так дешево.

Вот фрагмент кода:

  1. Родитель:

    import React, { useState, useEffect } from 'react';
    import {View, Text, Dimensions} from 'react-native';
    import MyTasksTab from './MyTasksTab';
    import TaskInvitationsTab from './TaskInvitationsTab';
    import MyApplicationTab from './MyApplicationTab';
    import { TabView, SceneMap, TabBar } from 'react-native-tab-view';
    import EStyleSheet from 'react-native-extended-stylesheet';
    
    //Get width of mobile screen
    var width = Dimensions.get("window").width;
    var height = Dimensions.get("window").height;
    
    const TasksMainScreen = (props) => {
        const { t, locale } = props.screenProps;
        const [index, setIndex] = useState(0);
        const [routes, setRoutes] = useState([
            { key: 'first', title: t('tasksMain.myTasksTabTitle') },
            { key: 'second', title: t('tasksMain.taskInvitationTabTitle') },
            { key: 'third', title: t('tasksMain.myApplicationsTabTitle') }
        ]);
    
        useEffect (() => {
            props.navigation.setParams({
                styles
            });
        },
        []);
    
        return (
            <View style={styles.container}>
                <TabView
                    navigationState={{index, routes}}
                    renderScene={SceneMap({
                        first: () => <MyTasksTab memoProps={memoProps}/>,
                        second: () => <TaskInvitationsTab {...props}/>,
                        third: () => <MyApplicationTab {...props}/>
                    })}
                    onIndexChange={index => setIndex(index)}
                    initialLayout={{width}}
                    renderTabBar={(props) =>
                        <TabBar
                            {...props}
                            activeColor={EStyleSheet.value('$tabNavActive')}
                            inactiveColor={EStyleSheet.value('$tabNavInactive')}
                            style={styles.tabView}
                            labelStyle={textStyles.text15}
                            indicatorStyle={styles.tabViewIndictor}
                        />
                    }
                />
            </View>
        );
    }
    
    export default TasksMainScreen;
    
  2. Ребенок:

    import React from 'react';
    import { View, FlatList, ActivityIndicator } from 'react-native';
    import { ApplicantsCard } from '../../../GlobalReusableComponents/SeemCards';
    import EmptyTab from '../EmptyTab';
    import firebase from '../../../util/firebaseConfig';
    import useGetMyTasks from '../../../context/useGetMyTasks';
    import dateFromSeconds from '../../../util/dateFromSeconds';
    import EStyleSheet from 'react-native-extended-stylesheet';
    
    const MyTasksTab = (props) => {
        const { 
            myTasks,
            isFetchMyTasks,
            fetchMyTasks 
        } = useGetMyTasks(firebase.auth().currentUser.uid);
    
        const _keyExtractor = (item, i) => `${item.date}-${i}`;
    
        const _renderApplicantsCard = ({item}) => {
            let {date, time} = dateFromSeconds(item.lastDateToApply.seconds);
            return (
                <View style={styles.cardContainer}>
                    <ApplicantsCard
                        date={date}
                        time={time}
                        priceText='Est Budget: '
                        price={item.estimatedBudget}
                        title={item.name}
                        description={item.yearsOfExperience}
                        state={item.city}
                        onPress={() => props.navigation.navigate('MyTaskDetails', {myTask: item})}
                        {...item}
                    />
                </View>
            );
    
        }
    
        if(!myTasks){
            return (
                <View style={{flex: 1, justifyContent: 'center'}}>
                    <ActivityIndicator color={EStyleSheet.value('$primaryColor')}/>
                </View>
            );
        }
    
        if(myTasks.length == 0){
            return (
                <EmptyTab 
                    image={require('../../../assets/FindExpert.png')}
                    details={`You don't have Tasks yet, Create a new Task now.`}
                    buttonText='Create A New Task'
                    onButtonPress={() => props.navigation.navigate('AboutTask')}
                    {...props}
                />
            );
        }
    
        return (
            <View style={styles.container}>
                <View style={styles.flatListContainer}>
                    <FlatList
                        data={myTasks}
                        keyExtractor={_keyExtractor}
                        renderItem={_renderApplicantsCard}
                        refreshing={isFetchMyTasks}
                        onRefresh={() => fetchMyTasks()}
                    />
                </View>
            </View>
        );  
    }
    
    export default MyTasksTab;
    
  3. Крюк для вызова myTasks Firestore Api:

    import { useEffect, useState } from 'react';
    import firebase from '../util/firebaseConfig';
    
    export default (id) => {
        const [myTasks, setMyTasks] = useState(null);
        const [isFetchMyTasks, setIsFetchMyTasks] = useState(false);
    
        const fetchMyTasks = () => {
            setIsFetchMyTasks(true);
            firebase.firestore().collection('tasks').where('user_id', '==', id).orderBy('createdAt', 'desc').get()
            .then((querySnapshot) => {
                let tasks = [];
                querySnapshot.forEach(function(document) {
                    tasks = [
                        ...tasks,
                        {
                            task_id: document.id,
                            ...document.data()
                        }
                    ]
                });
                setIsFetchMyTasks(false);
                setMyTasks(tasks);
            })
            .catch((error) => {
                setIsFetchMyTasks(false);
            })
        };
    
        useEffect(() => {
            fetchMyTasks();
        },
        [])
    
        return {
            myTasks,
            isFetchMyTasks,
            fetchMyTasks
        };
    };
    

Я искал день, пытаясь выяснить, что происходит, пока я получил тот факт, что useEffect срабатывает один раз при каждом рендеринге, если вы оставляете массив после функции пустым и если я хочу остановить это, я мог бы использовать memo для дочернего компонента и useMemo для запоминания примитивных значений, а useCallback для запоминания массивов и объектов, поэтому я попробовал этот подход с нет никакой надежды, поэтому я пошел с другой идеей, переместив мой хук API в родительский компонент и попытался передать реквизиты его дочернему элементу, чтобы предотвратить чрезмерный вызов API, но мой компонент Flatlist перерисовывается, вызывая прокрутку к началу поведения в каждом новом наборе данных к основному массиву данных, каковы доступные решения для этого случая ?! извините за то, что много пишу.

Я использую React Navigation и вот иерархия экрана:

Switch -> Tab -> Stack (здесь мой родитель) - >act-native-tab -view -> (вот мой ребенок)

Когда я перехожу к любому экрану на том же уровне навигации по вкладкам или более глубоко, я получаю консоль, например:

enter image description here

...