Как вы получаете подробные сведения о базе данных пользователя, связанные с authUser в Firestore? - PullRequest
10 голосов
/ 09 января 2020

Я пытаюсь выяснить, как получить имя пользователя, которое является атрибутом, хранящимся в коллекции пользователей, которая была объединена с атрибутами, созданными моделью аутентификации firebase.

Я могу получить доступ к authUser - который дает мне ограниченные поля, которые firebase собирает в инструменте аутентификации, и затем я пытаюсь перейти оттуда к связанной коллекции пользователей (которая использует тот же uid).

У меня есть потребитель реагирующего контекста с:

import React from 'react';
const AuthUserContext = React.createContext(null);
export default AuthUserContext;

Затем в моем компоненте я пытаюсь использовать:

const Test = () => (

<AuthUserContext.Consumer>
    {authUser => (

    <div>
            {authUser.email} // I can access the attributes in the authentication collection 
            {authUser.uid.user.name} //i cannot find a way to get the details in the related user collection document - where the uid on the collection is the same as the uid on the authentication table


     </div>
    )}
</AuthUserContext.Consumer>
);

const condition = authUser => !!authUser;
export default compose(
withEmailVerification,
withAuthorization(condition),
)(Test);

В моей базе данных. js - Я думаю, Я попытался объединить атрибуты authUser из модели Authentication с атрибутами пользовательской коллекции следующим образом:

class Firebase {
  constructor() {
    app.initializeApp(config).firestore();
    /* helpers */
    this.fieldValue = app.firestore.FieldValue;


    /* Firebase APIs */
    this.auth = app.auth();
    this.db = app.firestore();

onAuthUserListener = (next, fallback) =>
    this.auth.onAuthStateChanged(authUser => {
      if (authUser) {
        this.user(authUser.uid)
          .get()
          .then(snapshot => {
            const dbUser = snapshot.data();
            // default empty roles
            if (!dbUser.roles) {
              dbUser.roles = {};
            }
            // merge auth and db user
            authUser = {
              uid: authUser.uid,
              email: authUser.email,
              emailVerified: authUser.emailVerified,
              providerData: authUser.providerData,
              ...dbUser,
            };
            next(authUser);
          });
      } else {
        fallback();
      }
    });

Я не могу найти способ получить от authUser (который работает, чтобы получить меня к Атрибуты аутентификации) - для пользовательской коллекции, у которой есть идентификатор с тем же идентификатором из коллекции аутентификации.

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

Я пытался использовать помощник в своей базе данных. js, чтобы дать мне пользователя из uid - но это тоже не помогает.

user = uid => this.db.doc(`users/${uid}`);
  users = () => this.db.collection('users');

Следующая попытка

Чтобы добавить больше фона, я создал тестовый компонент, который может регистрировать (но не отображать) ) authUser, следующим образом:

import React, { Component } from 'react';
import { withFirebase } from '../Firebase/Index';
import { Button, Layout  } from 'antd';

import { AuthUserContext, withAuthorization, withEmailVerification } from '../Session/Index';


class Test extends Component {
  constructor(props) {
    super(props);

    this.state = {
      loading: false,
      user: null,
      ...props.location.state,
    };
  }

  componentDidMount() {
    if (this.state.user) {
      return;
    }

    this.setState({ loading: true });

    // this.unsubscribe = this.props.firebase
    //   .user(authUser.uid)
    //   .onSnapshot(snapshot => {
    //     const userData = snapshot.data();  
    //     console.log(userData);
    //     this.setState({
    //       user: snapshot.data(),
    //       loading: false,
    //     });
    //   });
  }

  componentWillUnmount() {
    this.unsubscribe && this.unsubscribe();
  }



  render() {
    const { user, loading } = this.state;


    return (
        <div>
        <AuthUserContext.Consumer>
        {authUser => (
            console.log(authUser),
            <p>
                </p>


            )}
            </AuthUserContext.Consumer> 

        </div>

    );

    };
}
export default Test;

В журнале отображаются подробные данные для uid, email и c в журналах, но он входит в длинный список элементов, многие из которых начинаются с 1 или 2 буквы (я не могу найти ключ, чтобы узнать, что означают эти префиксные буквы). Пример, извлеченный ниже:

enter image description here

ОБНОВЛЕНИЕ НА ЭТОМ КОММЕНТАРИИ:

Ранее я говорил: поля для uid, email et c похоже, что они не вложены в эти префиксы, но если я пытаюсь:

 console.log(authUser.email)

, я получаю сообщение об ошибке:

TypeError: Невозможно прочитать свойство 'email' из null

Обновление: Я только что понял, что в журнале консоли мне нужно развернуть выпадающее меню с надписью:

Q {I : Array (0), l:

, чтобы увидеть атрибут электронной почты. Кто-нибудь знает, на что намекает этот jibberi sh? Я не могу найти ключ, чтобы выяснить, что означает Q, I или l, чтобы узнать, должен ли я ссылаться на эти вещи, чтобы добраться до соответствующих атрибутов в таблице Authentication. Может быть, если я смогу это выяснить - я смогу найти способ добраться до коллекции пользователей, используя uid из коллекции Authentication.

Кто-нибудь использовал реакцию на интерфейс, с потребителем контекста, чтобы узнать, кто текущий пользователь есть? Если да, то как вы получаете доступ к их атрибутам в модели аутентификации и как вы обращались к атрибутам в связанной коллекции пользователей (где docId в документе пользователя является идентификатором пользователя из таблицы аутентификации)?

СЛЕДУЮЩАЯ ПОПЫТКА

Следующая попытка дала очень странный результат.

У меня есть две отдельные страницы, которые являются потребителями контекста. Разница между ними заключается в том, что один является функцией, а другой - компонентом класса.

В компоненте функции я могу выполнить {authUser.email}. Когда я пытаюсь сделать то же самое в компоненте класса, я получаю сообщение об ошибке:

TypeError: Невозможно прочитать свойство 'email' со значением null

Эта ошибка приходит из того же сеанса с тем же вошедшим в систему пользователем.

Примечание: хотя в документации firebase сказано, что свойство currentUser доступно при авторизации - я не смог заставить его работать вообще.

Мой функциональный компонент имеет:

import React from 'react';
import { Link } from 'react-router-dom';
import { compose } from 'recompose';
import { AuthUserContext, withAuthorization, withEmailVerification } from '../Session/Index';


const Account = () => (

<AuthUserContext.Consumer>
    {authUser => (
    <div>
         {authUser.email}
    </div>
    )}
</AuthUserContext.Consumer>
);

// const condition = authUser => !!authUser;
// export default compose(
// withEmailVerification,
// withAuthorization(condition),
// )(Account);
export default Account;

Хотя я не могу добраться до атрибутов коллекции User, где docId в пользовательском документе совпадает с uid аутентифицированного пользователя, из этого компонента я могу вывести атрибут email в коллекции auth для этого пользователя.

Хотя документация Firebase предоставляет этот совет для управления пользователями и доступа к атрибутам, я не нашел способа реализовать этот подход в реакции. Каждый вариант попытки сделать это, как с помощью помощников в моем firebase. js, так и с попытки начать с нуля в компоненте, приводит к ошибкам в доступе к firebase. Тем не менее, я могу создать список пользователей и связанную с ними информацию о коллекции пользователей (я не могу получить пользователя на основе того, кто является authUser).

Мой компонент класса имеет:

import React from 'react';
import {
    BrowserRouter as Router,
    Route,
    Link,
    Switch,

  } from 'react-router-dom';
import * as ROUTES from '../../constants/Routes';
import { compose } from 'recompose';
import { withFirebase } from '../Firebase/Index';
import { AuthUserContext, withAuthorization, withEmailVerification } from '../Session/Index';



class Dashboard extends React.Component {
  state = {
    collapsed: false,
  };

  onCollapse = collapsed => {
    console.log(collapsed);
    this.setState({ collapsed });
  };

  render() {
    const {  loading } = this.state;
    // const dbUser = this.props.firebase.app.snapshot.data();
    // const user = Firebase.auth().currentUser;
    return (
    <AuthUserContext.Consumer>
      {authUser => (  

        <div>    
         {authUser.email} // error message as shown above
          {console.log(authUser)} // output logged in amongst a long list of menus prefixed with either 1 or 2 characters. I can't find a key to decipher what these menus mean or do.
        </div>
      )}
    </AuthUserContext.Consumer>  
    );
  }
}

//export default withFirebase(Dashboard);
export default Dashboard;

В моем AuthContext.Provider - у меня есть:

import React from 'react';
import { AuthUserContext } from '../Session/Index';
import { withFirebase } from '../Firebase/Index';
const withAuthentication = Component => {
  class WithAuthentication extends React.Component {
    constructor(props) {
      super(props);
      this.state = {
        authUser: null,
      };  
    }

    componentDidMount() {
      this.listener = this.props.firebase.auth.onAuthStateChanged(
        authUser => {
          authUser
            ? this.setState({ authUser })
            : this.setState({ authUser: null });
        },
      );
    }

    componentWillUnmount() {
      this.listener();
    };  

    render() {
      return (
        <AuthUserContext.Provider value={this.state.authUser}>
          <Component {...this.props} />
        </AuthUserContext.Provider>
      );
    }
  }
  return withFirebase(WithAuthentication);

};
export default withAuthentication;

СЛЕДУЮЩАЯ ПОПЫТКА

Странно, что с этой попыткой я пытаюсь сохранить в журнале значения, которые, как я вижу, существуют в базе данных, и значение name возвращается как «неопределенное», где в базе данных есть строка.

Эта попытка имеет:

    import React from 'react';
    import {
        BrowserRouter as Router,
        Route,
        Link,
        Switch,
        useRouteMatch,
     } from 'react-router-dom';
    import * as ROUTES from '../../constants/Routes';
    import { compose } from 'recompose';
    import { withFirebase } from '../Firebase/Index';
    import { AuthUserContext, withAuthorization, withEmailVerification } from '../Session/Index';



    class Dash extends React.Component {
      // state = {
      //   collapsed: false,
      // };

      constructor(props) {
        super(props);

        this.state = {
          collapsed: false,
          loading: false,
          user: null,
          ...props.location.state,
        };
      }
      componentDidMount() {
        if (this.state.user) {
          return;
        }

        this.setState({ loading: true });

        this.unsubscribe = this.props.firebase
          .user(this.props.match.params.id)
          // .user(this.props.user.uid)
          // .user(authUser.uid)
          // .user(authUser.id)
          // .user(Firebase.auth().currentUser.id)
          // .user(Firebase.auth().currentUser.uid)

          .onSnapshot(snapshot => {
            this.setState({
              user: snapshot.data(),
              loading: false,
            });
          });
      }

      componentWillUnmount() {
        this.unsubscribe && this.unsubscribe();
      }


      onCollapse = collapsed => {
        console.log(collapsed);
        this.setState({ collapsed });
      };

      render() {
        // const {  loading } = this.state;
        const { user, loading } = this.state;
        // let match = useRouteMatch();
        // const dbUser = this.props.firebase.app.snapshot.data();
        // const user = Firebase.auth().currentUser;
        return (
        <AuthUserContext.Consumer>
          {authUser => (  

            <div>    
            {loading && <div>Loading ...</div>}

                <Layout style={{ minHeight: '100vh' }}>
                  <Sider collapsible collapsed={this.state.collapsed} onCollapse={this.onCollapse}>
                    <div  />

                  </Sider>
                <Layout>

                    <Header>
                    {console.log("authUser:", authUser)}
                    // this log returns the big long list of outputs - the screen shot posted above is an extract. It includes the correct Authentication table (collection) attributes
                    {console.log("authUser uid:", authUser.uid)}
                    // this log returns the correct uid of the current logged in user
                    {console.log("Current User:", this.props.firebase.auth.currentUser.uid)}
// this log returns the correct uid of the current logged in user
                    {console.log("current user:", this.props.firebase.db.collection("users").doc(this.props.firebase.auth.currentUser.uid
                    ))}
// this log returns a big long list of things under a heading: DocumentReference {_key: DocumentKey, firestore: Firestore, _firestoreClient: FirestoreClient}. One of the attributes is: id: (...) (I can't click to expand this).
                    {console.log("current user:", this.props.firebase.db.collection("users").doc(this.props.firebase.auth.currentUser.uid
                    ).name)}
//this log returns: undefined. There is an attribute in my user document called 'name'. It has a string value on the document with the id which is the same as the currentUser.uid.
                    <Text style={{ float: 'right', color: "#fff"}}>

                      {user && (
                        <Text style={{ color: "#fff"}}>{user.name}
//this just gets skipped over in the output. No error but also does not return the name.
</Text>


                      )}

                    </Text>
                    </Header>      
                   </Layout>
                </Layout>

            </div>
          )}
        </AuthUserContext.Consumer>  
        );
      }
    }

    export default withFirebase(Dash);

СЛЕДУЮЩАЯ ПОПЫТКА

Так что эта попытка неуклюжа и не использует хелперы или запросы снимков что я пытался использовать выше, но зарегистрируйте атрибуты документа коллекции пользователей на консоли следующим образом:

{this.props.firebase.db.collection ('users'). do c (authUser.uid ) .get ()

      .then(doc => {
          console.log(doc.data().name) 
      })

    } 

Что я не могу сделать, так это найти способ отрисовки этого имени в jsx

Как вы на самом деле печатаете вывод?

Когда я пытаюсь:

{ 


this.props.firebase.db.collection('users').doc(authUser.uid).get().data().name

                }

Я получаю сообщение об ошибке:

Ошибка типа: this.props.firebase.db.collection (...). Do * +1194 * (...). get (...). data не является функцией

Когда я пытаюсь:

{ 



this.props.firebase.db.collection('users').doc(authUser.uid).get()
              .then(doc => {
                  console.log(doc.data().name), 
                  <p>doc.data().name</p>
              })
            } 

я получаю сообщение об ошибке:

Строка 281: 23: ожидал присваивания или вызова функции и вместо этого видел выражение no-unused-expression *

Когда я пытаюсь:

{ 


this.props.firebase.db.collection('users').doc(authUser.uid).get("name")
              .then(doc => {
                  console.log(doc.data().name), 
                  <p>doc.data().name</p>
              })
            }

Сообщение об ошибке гласит:

Ожидал назначения или вызова функции и вместо этого увидел выражение

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

СЛЕДУЮЩАЯ ПОПЫТКА

Я нашел этот пост . В нем есть хорошее объяснение того, что должно произойти, но я не могу реализовать это, как показано, потому что componentDidMount не знает, что такое authUser.

Моя текущая попытка заключается в следующем - однако, как написано в настоящее время, authUser обертка для возвращаемого значения - и сегмент componentDidMount не знает, что такое authUser.

import React from 'react';
import {
    BrowserRouter as Router,
    Route,
    Link,
    Switch,
    useRouteMatch,
 } from 'react-router-dom';
import * as ROUTES from '../../constants/Routes';
import { compose } from 'recompose';
import { Divider, Layout, Card, Tabs, Typography, Menu, Breadcrumb, Icon } from 'antd';
import { withFirebase } from '../Firebase/Index';
import { AuthUserContext, withAuthorization, withEmailVerification } from '../Session/Index';




const { Title, Text } = Typography
const { TabPane } = Tabs;
const { Header, Content, Footer, Sider } = Layout;
const { SubMenu } = Menu;


class Dashboard extends React.Component {
  // state = {
  //   collapsed: false,
  //   loading: false,
  // };

  constructor(props) {
    super(props);

    this.state = {
      collapsed: false,
      loading: false,
      user: null,
      ...props.location.state,
    };
  }
  componentDidMount() {
    if (this.state.user) {
      return;
    }

    this.setState({ loading: true });

    this.unsubscribe = this.props.firebase
      .user(this.props.match.params.id)
      .onSnapshot(snapshot => {
        this.setState({
          user: snapshot.data(),
          loading: false,
        });
      });
  // }

//   firebase.firestore().collection("users")
//     .doc(this.state.uid)
//     .get()
//     .then(doc => {
//       this.setState({ post_user_name: doc.data().name });
//   });
// }

  this.props.firebase.db
    .collection('users')
    .doc(authUser.uid)
    .get()
    .then(doc => {
        this.setState({ user_name: doc.data().name });
        // loading: false,
      });  
    }                  

  componentWillUnmount() {
    this.unsubscribe && this.unsubscribe();
  }


  onCollapse = collapsed => {
    console.log(collapsed);
    this.setState({ collapsed });
  };

  render() {
    // const {  loading } = this.state;
    // const { user, loading } = this.state;
    // let match = useRouteMatch();
    // const dbUser = this.props.firebase.app.snapshot.data();
    // const user = Firebase.auth().currentUser;


    return (
    <AuthUserContext.Consumer>
      { authUser => (  

        <div>    

                <Header>

                 {/* 
                    { 
                    this.props.firebase.db.collection('users').doc(authUser.uid).get()
                    .then(doc => {
                        console.log( doc.data().name
)                          
                    })
                  } 
                  */} 


                  </Text>
                </Header>      

                      <Switch>

                      </Switch>    

        </div>
      )}
    </AuthUserContext.Consumer>  
    );
  }
}

export default withFirebase(Dashboard);

СЛЕДУЮЩАЯ ПОПЫТКА

Далее я попытался обернуть маршрут для панель инструментов внутри AuthContext.Consumer, чтобы его мог использовать весь компонент, что позволило мне получить доступ к зарегистрированному пользователю в функции componentDidMount.

Я изменил маршрут на:

<Route path={ROUTES.DASHBOARD} render={props => (
          <AuthUserContext.Consumer>
             { authUser => ( 
                <Dashboard authUser={authUser} {...props} />  
             )}
          </AuthUserContext.Consumer>
        )} />

и удалил потребителя из оператора рендеринга компонента панели мониторинга.

Затем в componentDidMount компонента Dashboard я попытался:

componentDidMount() {
    if (this.state.user) {
      return;
    }

    this.setState({ loading: true });

     this.unsubscribe =
     this.props.firebase.db
     .collection('users')
   //.doc(this.props.firebase.db.collection('users').doc(this.props.firebase.authUser.uid))
 .doc(this.props.firebase.db.collection('users').doc(this.props.authUser.uid))
     .get()
     .then(doc => {
         this.setState({ name: doc.data().name });
       loading: false,
      });  
  }                  

При попытке этого я получаю сообщение об ошибке:

FirebaseError: Функция CollectionReference.do c () требует, чтобы ее первый аргумент имел тип непустой строки, но было: пользовательский объект DocumentReference

СЛЕДУЮЩАЯ ПОПЫТКА Представленные ниже люди, кажется, находят что-то полезное в первом предложенном решении. Я не смог найти в нем ничего полезного, но, читая его предложения, я изо всех сил пытаюсь понять, как работает пример в документации по firebase (он не раскрывает, как присвоить .do * значение для .do c () запрос), который выглядит следующим образом:

db.collection("cities").doc("SF");

  docRef.get().then(function(doc) {
      if (doc.exists) {
          console.log("Document data:", doc.data());
      } else {
          // doc.data() will be undefined in this case
          console.log("No such document!");
      }

в корне отличается от моей попытки в функции componentDidMount, а именно:

this.unsubscribe =
  this.props.firebase.db
    .collection('users')
    // .doc(this.props.firebase.db.collection('users').doc(this.props.firebase.authUser.uid))
    // .doc(this.props.firebase.db.collection('users').uid: this.props.firebase.auth().currentUser.uid  )
    .doc(this.props.authUser.uid)
    .get()
    .then(doc => {
        this.setState({ user.name: doc.data().name });
        // loading: false,
      }else {
        // doc.data() will be undefined in this case
        console.log("Can't find this record");
      }

    );  
  }

Возможно, решение этого шага является подсказкой, которая поможет продвинуть его к результату. Может ли кто-нибудь найти лучшую документацию по пожарному хранилищу, чтобы показать, как получить запись коллекции пользователей, используя зарегистрированный пользовательский uid слушателя?

С этой целью я вижу в кодовой лаборатории FriendlyEats пример , что есть попытка присвоить do c .id значение поиска id в коде. Я не знаю, на каком языке написан этот код - но он действительно похож на то, что я пытаюсь сделать - я просто не понимаю, как перейти от этого примера к тому, с чем я знаю, как работать.

display: function(doc) {
      var data = doc.data();
      data['.id'] = doc.id;
      data['go_to_restaurant'] = function() {
        that.router.navigate('/restaurants/' + doc.id);
      };

Ответы [ 4 ]

5 голосов
/ 09 января 2020

Я понимаю из последней строки вашего вопроса (users = () => this.db.collection('users');), что коллекция, в которой вы храните дополнительную информацию о пользователях, называется users и что пользовательский документ в этой коллекции использует userId (uid) в качестве docId.

Следующие действия должны выполнить (не проверено):

class Firebase {
  constructor() {
    app.initializeApp(config).firestore();
    /* helpers */
    this.fieldValue = app.firestore.FieldValue;


    /* Firebase APIs */
    this.auth = app.auth();
    this.db = app.firestore();

onAuthUserListener = (next, fallback) =>
    this.auth.onAuthStateChanged(authUser => {
      if (authUser) {
           this.db.collection('users').doc(authUser.uid)
              .get()
              .then(snapshot => {
                const userData = snapshot.data();
                console.log(userData);
                //Do whatever you need with userData
                //i.e. merging it with authUser
                //......

                next(authUser);
          });
      } else {
        fallback();
      }
    });

Итак, в пределах наблюдателя, установленного с помощью метода onAuthStateChanged(), когда мы обнаруживаем, что пользователь вошел в систему (т.е. в if (authUser) {}), мы используем его uid для запроса уникального документа, соответствующего этому пользователю в коллекции users (см. чтение одного документа и do c из get() метод).

1 голос
/ 14 января 2020

У меня есть теория, которую я хотел бы, чтобы вы проверили.

Я думаю, что когда вы вызываете next(authUser) внутри вашего обработчика onAuthStateChanged, во время его выполнения возникает ошибка (например, cannot read property 'name' of undefined at ...).

Причина, по которой ваш код не работает должным образом, заключается в том, что, когда вы звоните next(authUser), он находится внутри then() цепочки Promise. Любые ошибки, возникшие в Обещании, будут обнаружены и приведут к отклонению Обещания. Когда Promise отклоняется, он вызывает вызывающие ошибки обработчики ошибок. В рассматриваемой цепочке Promise в настоящее время нет такого обработчика ошибок.

Если я вас потерял, прочитайте это сообщение в блоге для курса Promises cra sh и затем возвращайтесь.

Так, что мы можем сделать, чтобы избежать такой ситуации? Проще всего было бы вызвать next(authUser) вне области действия обработчика Promise then(). Мы можем сделать это, используя window.setTimeout(function).

Таким образом, в вашем коде вы должны заменить

next(authUser)

на

setTimeout(() => next(authUser))
// or setTimeout(() => next(authUser), 0) for the same result

Это будет генерировать любые ошибки как обычно, а не перехватывать их цепочкой Promise.

Важно, что у вас нет обработчика перехвата, который обрабатывает, когда userDocRef.get() не удается. Так что просто добавьте .catch(() => setTimeout(fallback)) в конце then(), чтобы в вашем коде использовался резервный метод, если он выдаст ошибку.

Итак, в итоге мы получим:

this.user(authUser.uid)
  .get()
  .then(snapshot => {
    const dbUser = snapshot.data();
    // default empty roles
    if (!dbUser.roles) {
      dbUser.roles = {};
    }
    // merge auth and db user
    authUser = {
      ...dbUser, // CHANGED: Moved dbUser to beginning so it doesn't override other info
      uid: authUser.uid,
      email: authUser.email,
      emailVerified: authUser.emailVerified,
      providerData: authUser.providerData
    };
    setTimeout(() => next(authUser), 0); // invoke callback outside of Promise
  })
  .catch((err) => setTimeout(() => fallback(), 0)); // invoke callback outside of Promise

Отредактированный код

Приведенное выше объяснение должно позволить вам исправить ваш код, но вот моя версия вашего Firebase класса с различными изменениями в простоте использования.

Использование:

import FirebaseHelper from './FirebaseHelper.js';

const fb = new FirebaseHelper();
fb.onUserDataListener(userData => {
  // do something - user is logged in!
}, () => {
  // do something - user isn't logged in or an error occurred
}

Определение класса:

// granular Firebase namespace import
import firebase from 'firebase/app';
import 'firebase/auth';
import 'firebase/firestore';

const config = { /* firebase config goes here */ };

export default class FirebaseHelper { // renamed from `Firebase` to prevent confusion
  constructor() {
    /* init SDK if needed */
    if (firebase.apps.length == 0) { firebase.initializeApp(config); }

    /* helpers */
    this.fieldValue = app.firestore.FieldValue;

    /* Firebase APIs */
    this.auth = firebase.auth();
    this.db = firebase.firestore();
  }

  getUserDocRef(uid) { // renamed from `user`
    return this.db.doc(`users/${uid}`);
  }

  getUsersColRef() { // renamed from `users`
    return this.db.collection('users');
  }

  /**
   * Attaches listeners to user information events.
   * @param {function} next - event callback that receives user data objects
   * @param {function} fallback - event callback that is called on errors or when user not logged in
   *
   * @returns {function} unsubscribe function for this listener
   */
  onUserDataListener(next, fallback) {
    return this.auth.onAuthStateChanged(authUser => {
      if (!authUser) {
        // user not logged in, call fallback handler
        fallback();
        return;
      }

      this.getUserDocRef(authUser.uid).get()
        .then(snapshot => {
          let snapshotData = snapshot.data();

          let userData = {
            ...snapshotData, // snapshotData first so it doesn't override information from authUser object
            uid: authUser.uid,
            email: authUser.email,
            emailVerified: authUser.emailVerifed,
            providerData: authUser.providerData
          };

          setTimeout(() => next(userData), 0); // escapes this Promise's error handler
        })
        .catch(err => {
          // TODO: Handle error?
          console.error('Error while getting user document -> ', err.code ? err.code + ': ' + err.message : (err.message || err));
          setTimeout(fallback, 0); // escapes this Promise's error handler
        });
    });
  }

  // ... other methods ...
}

Обратите внимание, что в этой версии метод onUserDataListener возвращает функцию отмены подписки из onAuthStateChanged. Когда ваш компонент отключен, вы должны отсоединить все соответствующие прослушиватели, чтобы не было утечки памяти или неработающего кода, работающего в фоновом режиме, когда он не нужен.

class SomeComponent {
  constructor() {
    this._unsubscribe = fb.onUserDataListener(userData => {
      // do something - user is logged in!
    }, () => {
      // do something - user isn't logged in or an error occurred
    };
  }

  // later
  componentWillUnmount() {
    this._unsubscribe();
  }
}
0 голосов
/ 27 января 2020

Если кто-то еще застрял подобным образом, я нашел решение здесь: Firebase & React: CollectionReference.do c () тип аргумента

Не работает при обновлении страницы sh (по-прежнему выдает ошибку о том, что uid равен null), но для реакции перехватываний на использование useEffect следует заменить функцию componentDidMount комбинацией Mount и Update. Я пытаюсь это потом.

0 голосов
/ 24 января 2020

В вашей реализации AuthContext.Provider вы получаете прямой доступ к прослушивателю SDK onAuthStateChanged:

componentDidMount() {
  this.listener = this.props.firebase.auth.onAuthStateChanged(
    authUser => {
      authUser
        ? this.setState({ authUser })
        : this.setState({ authUser: null });
    }
  );
}

Это должно быть изменено для использования onAuthUserListener из вашего вспомогательного класса:

componentDidMount() {
  this.listener = this.props.firebase.onAuthUserListener(
    /* next()     */ (authUserWithData) => this.setState({authUser: authUserWithData}),
    /* fallback() */ () => this.setState({authUser: null})
  );
}

Что касается сообщений журнала, заполненных множеством случайных свойств, это связано с тем, что объект firebase.User имеет как publi c API , так и реализацию с рядом частных свойств и методы, которые минимизируются при компиляции. Поскольку эти минимизированные свойства и методы явно не помечены как не перечисляемые, они включаются в любой вывод журнала. Если вместо этого вы хотите регистрировать только те части, которые действительно полезны, вы можете деструктурировать и реструктурировать объект, используя:

// Extracts public properties of firebase.User objects
// see https://firebase.google.com/docs/reference/js/firebase.User#properties
function extractPublicProps(user) {
  let {displayName, email, emailVerified, isAnonymous, metadata, phoneNumber, photoURL, providerData, providerId, refreshToken, tenantId, uid} = user;
  return {displayName, email, emailVerified, isAnonymous, metadata, phoneNumber, photoURL, providerData, providerId, refreshToken, tenantId, uid}
}

function extractUsefulProps(user) {
  let {displayName, email, emailVerified, isAnonymous, phoneNumber, photoURL, uid} = user;
  return {displayName, email, emailVerified, isAnonymous, phoneNumber, photoURL, uid}
}

let authUser = firebase.auth().currentUser;
console.log(authUser);
console.log(extractPublicProps(authUser));
console.log(extractUsefulProps(authUser));
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...