React Native useEffect вызывает слушателей Firestore более одного раза - PullRequest
0 голосов
/ 20 октября 2019

Я делаю мобильное приложение, используя React Native (0.61.2) и TypeScript (3.6.4). Я использую Firebase Authentication и Firebase Firestore из React Native Firebase collection.

Я пытаюсь:

  1. Прослушивание изменений состояния аутентификации (с помощью onAuthStateChanged)
  2. Прослушивание изменений аутентификации пользователя (с использованием onUserChanged)
  3. Прослушивание документа пользователя (с использованием onSnapshot)
  4. Прослушивание определенного документа под названием brand (с использованием onSnapshot)

Я сохраняю в контексте userAuth, документ пользователя и документ бренда от предыдущих слушателей.

/**
 * Types
 */
type UserAuth = FirebaseAuthTypes.User | null;
type User = UserDoc | null;
type Brand = BrandDoc | null;
type ContextProps = {
  userAuth: UserAuth;
  user: User;
  brand: Brand;
} | null;

/**
 * Context
 */
export const Context = createContext<ContextProps>(null);

function App() {
  const [initializing, setInitializing] = useState(true);

  const [listenUserAuth, setListenUserAuth] = useState(false);
  const [userAuth, setUserAuth] = useState<UserAuth>(null);

  const [listenUser, setListenUser] = useState(false);
  const [user, setUser] = useState<User>(null);

  const [listenBrand, setListenBrand] = useState(false);
  const [brand, setBrand] = useState<Brand>(null);

  /** Listen for auth state changes */
  useEffect(() => {
    const authListener = auth().onAuthStateChanged(result => {
      setUserAuth(result);
      if (initializing && !listenUserAuth) {
        setInitializing(false);
        setListenUserAuth(true);
        setListenUser(true);
        setListenBrand(true);
      }
    });

    return () => {
      if (authListener) {
        console.log('removing auth state listener');
        authListener();
      }
    };
  }, [initializing, listenUserAuth]);

  /** Listen for user auth changes */
  useEffect(() => {
    let userAuthListener: () => void;

    if (listenUserAuth) {
      userAuthListener = auth().onUserChanged(result => {
        setUserAuth(result);
      });
    }

    return () => {
      if (userAuthListener) {
        console.log('removing user auth listener');
        userAuthListener();
      }
    };
  }, [listenUserAuth]);

  /** Listen for user document changes */
  useEffect(() => {
    let userListener: () => void;

    if (listenUser) {
      if (!userAuth) {
        return;
      }

      console.log('listening user document');
      userListener = firestore()
        .collection('users')
        .doc(userAuth.uid)
        .onSnapshot(querySnapshot => {
          console.log('User querySnapshot: ', querySnapshot.data());
          setUser(querySnapshot.data());
        });
    }

    return () => {
      if (userListener) {
        console.log('removing user document listener');
        userListener();
      }
    };
  }, [listenUser, userAuth]);

  /** Listen for brand document changes */
  useEffect(() => {
    let brandListener: () => void;

    if (listenBrand) {
      if (!userAuth) {
        return;
      }

      console.log('listening brand document');
      brandListener = firestore()
        .collection('brands')
        .doc('31uOUtUkVYg8z953UdxS')
        .onSnapshot(querySnapshot => {
          console.log('Brand querySnapshot: ', querySnapshot.data());
          setBrand(querySnapshot.data());
        });
    }

    return () => {
      if (brandListener) {
        console.log('removing brand document listener');
        brandListener();
      }
    };
  }, [listenBrand, userAuth]);

  if (initializing) {
    return (
      <View style={{flex: 1, justifyContent: 'center'}}>
        <ActivityIndicator size="large" color="#000" />
      </View>
    );
  }

  function container(children: ReactNode | ReactNode[]) {
    return <PaperProvider theme={theme}>{children}</PaperProvider>;
  }

  return container(
    userAuth && user && brand ? (
      <Context.Provider value={{userAuth, user, brand}}>
        <SignedInStack />
      </Context.Provider>
    ) : (
      <SignedOutStack />
    )
  );
}

export default App;

В другом компоненте, когда пользователь выходит из системы, все слушатели снова слушают и удаляютсам по себе несколько раз.

Журналы:

enter image description here

Это добавляет ненужные операции чтения и записи в Firebase, которыеувеличить расходы.

Я новичок в использовании useEffect , и я почти уверен, что делаю что-то не так. Я не совсем понимаю зависимости, необходимые для использования useEffect для начала прослушивания документа пользователя и документа бренда и удаления слушателей один раз. Я попытался прочитать, прежде чем сделать это, и я подумал, что все правильно.

  • Кроме того, по какой-то причине, которую я не вижу, ActivityIndicator удаляется без документа пользователяи документ бренда. Он должен отображаться до тех пор, пока не будут заданы параметры UserAuth, User и Brand.

Моя цель здесь:

  1. Показывать ActivityIndicator до тех пор, пока не будут установлены все прослушиватели.
  2. Прослушивайте и удаляйте слушателей только один раз, чтобы избежать ненужных операций чтения и записи.
...