Firestore의 authUser와 관련된 사용자 DB 세부 정보는 어떻게 얻습니까?


10

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);

내 firebase.js에서-인증 모델의 authUser 속성을 다음과 같이 사용자 컬렉션 속성과 병합하려고했습니다.

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 (인증 속성으로 연결되도록 작동)에서 인증 콜렉션과 동일한 uid를 가진 사용자 콜렉션으로 이동하는 방법을 찾을 수 없습니다.

나는 이 게시물 을 보았습니다. 이 게시물 은 같은 문제가있는 것으로 보이며 대답이 암시 해야하는 것을 해결하려고 시도했지만 인증 컬렉션에서 사용자 컬렉션으로 가져 오는 방법을 찾지 못하는 것 같습니다. authUser에서 사용자 컬렉션의 속성에 액세스 할 수없는 경우 병합이 나를 위해 무엇을하고 있는지 알 수 없습니다.

firebase.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, 전자 메일 등에 대한 세부 정보가 표시되지만 긴 항목 목록에 있습니다. 대부분은 1 또는 2 문자로 시작합니다 (이 접두사 각각이 무엇인지 알 수있는 키를 찾을 수 없습니다) 글자 의미). 아래에서 추출한 예 :

여기에 이미지 설명을 입력하십시오

이 의견에 대한 업데이트 :

이전에 나는 말했다 : uid, email 등의 필드는 이러한 접두사 아래에 중첩되지 않은 것처럼 보이지만 다음과 같이하려고하면 :

 console.log(authUser.email)

, 다음과 같은 오류가 발생합니다.

TypeError : null의 속성 '이메일'을 읽을 수 없습니다

업데이트 : 방금 콘솔 로그에서 레이블이 지정된 드롭 다운 메뉴를 확장해야한다는 것을 깨달았습니다.

Q {I : 배열 (0), l :

이메일 속성을 확인하십시오. 이 혼란스러운 점을 아는 사람이 있습니까? 인증 테이블의 관련 속성을 얻기 위해 이러한 것들을 참조해야하는지 알기 위해 Q, I 또는 l의 의미를 알아내는 열쇠를 찾을 수 없습니다. 어쩌면 내가 알아낼 수 있다면 Authentication 컬렉션의 uid를 사용하여 사용자 컬렉션에 액세스하는 방법을 찾을 수 있습니다.

현재 사용자가 누구인지 알아 내기 위해 컨텍스트 소비자와 함께 프런트 엔드에서 반응 한 사람이 있습니까? 그렇다면 인증 모델에서 해당 속성에 어떻게 액세스하고 관련 사용자 콜렉션 (사용자 문서의 docId가 인증 테이블의 uid 임)의 속성에 어떻게 액세스 했습니까?

다음 공격

다음 시도는 매우 이상한 결과를 낳았습니다.

컨텍스트 소비자 인 2 개의 개별 페이지가 있습니다. 차이점은 하나는 함수이고 다른 하나는 클래스 구성 요소입니다.

함수 구성 요소에서 {authUser.email}을 렌더링 할 수 있습니다. 클래스 구성 요소에서 동일한 작업을 수행하려고하면 다음과 같은 오류가 발생합니다.

TypeError : null의 속성 '이메일'을 읽을 수 없습니다

이 오류는 동일한 로그인 사용자와 동일한 세션에서 발생합니다.

참고 : Firebase 설명서에 auth에서 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;

이 문서에서 사용자 문서의 docId가 인증 된 사용자의 uid와 동일한 User collection 속성을 얻을 수 없지만이 사용자의 auth collection에 email 속성을 출력 할 수 있습니다.

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 값이 '정의되지 않음'으로 반환되고 db에 문자열이있는 경우 정말 이상합니다.

이 시도는 다음과 같습니다.

    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'). doc (authUser.uid) .get ()

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

    } 

그래도 할 수없는 것은 jsx에서 해당 이름을 렌더링하는 방법을 찾는 것입니다.

실제로 출력물을 어떻게 인쇄합니까?

내가 시도 할 때 :

{ 


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

                }

다음과 같은 오류가 발생합니다.

TypeError : this.props.firebase.db.collection (...). doc (...). get (...). data는 함수가 아닙니다

내가 시도 할 때 :

{ 



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

다음과 같은 오류가 발생합니다.

Line 281 : 23 : 할당 또는 함수 호출을 예상하고 대신 사용하지 않은 표현식을 보았습니다.

내가 시도 할 때 :

{ 


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>
        )} />

대시 보드 구성 요소 렌더링 문에서 소비자를 제거했습니다.

그런 다음 Dashboard 구성 요소의 componentDidMount에서 다음을 시도했습니다.

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 : Function CollectionReference.doc ()에는 첫 번째 인수가 비어 있지 않은 문자열 유형이어야하지만 사용자 정의 DocumentReference 객체입니다.

다음 공격 아래 사람들은 첫 번째 제안 된 솔루션에 도움이 될 것 같습니다. 유용한 정보를 찾을 수 없었지만 제안 사항을 다시 읽으면서 Firebase 설명서의 예제를 확인하는 데 어려움을 겪고 있습니다 (.doc () 요청에 : uid 값을 제공하는 방법은 공개하지 않음) )는 다음과 같습니다.

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를 사용하여 사용자 콜렉션 레코드를 얻는 방법을 보여주는 더 나은 firestore 문서를 찾을 수 있습니까?

이를 위해 FriendlyEats 코드 랩 예제 에서 코드 의 id 검색 값에 doc.id를 제공하려는 시도가 있음을 알 수 있습니다. 이 코드가 어떤 언어로 작성되었는지는 알지 못합니다. 그러나 시도하고있는 것과 비슷해 보입니다. 예제에서 작업 방법을 알고있는 것으로 이동하는 방법을 볼 수 없습니다.

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

참고로 귀하의 용어는 옳지 않으며이 질문을 읽기 어렵게 만듭니다. Firebase에는 '테이블'이라는 것이 없습니다. Firebase 인증에는 '인증 ​​테이블'이없는 사용자 만 있습니다. Firestore에는 컬렉션과 해당 컬렉션 내에 문서가 있지만 테이블은 없습니다. 나는 당신이 어디에서 붙어 있는지 그리고 당신이 보여준 코드가 어떻게 예상대로 작동하지 않는지 알아 내려고 노력하고 있지만, 나는 그것을 함께 묶지 않고 있습니다. 문서를 찾을 수있는보다 표준적인 용어를 사용하고 예상대로 작동하지 않는 항목에 대해 더 명확하게 질문을 편집하십시오.
더그 스티븐슨

좋아-컬렉션을 대신하여 테이블을 대신 사용하십시오. 요점은 여전히 ​​같습니다.

내 요점은 실제로 당신의 요점을 얻을 수 없었고 용어가 도움이되지 않았다는 것입니다. 보여준 코드에서 무엇이 작동하지 않는지 더 자세히 설명해 주시겠습니까? 예상대로 작동하지 않았습니까? 설명 할 오류나 디버그 로그가 있습니까?
더그 스티븐슨

아무것도 작동하지 않습니다. authUser 리스너에서 사용자 컬렉션 세부 정보에 액세스하는 방법을 찾고 있습니다. authUser는 컨텍스트 핸들러 및 메소드의 변경 사항을 청취하는 관련 클래스 메소드에 정의되어 있습니다. 인증 콜렉션의 속성을 벗어날 수 없습니다. firestore의 관련 사용자 콜렉션에 액세스하려고합니다. 로그가 없습니다. 필드가 정의되지 않았다는 오류 메시지 만 있습니다.

1
간단한 작업부터 시작하여 경험을 쌓기 위해 노력하고 현재와 같은 더 복잡한 문제에 적용하는 것이 좋습니다. 문서에 대해 근본적으로 잘못된 것은 없습니다 (항상 사용하고 있기 때문에 사람들에게 도움이되기 때문에 알고 있습니다). 스택 오버플로에 대한 도움을 받으려면 특정 문제, 이상적으로는 누구나 문제를 재현하는 데 사용할 수있는 MCVE를 설명해야합니다. "나는 그것을 작동시킬 수 없다"고 말하는 것만으로는 충분하지 않습니다. stackoverflow.com/help/minimal-reproducible-example
Doug Stevenson

답변:


5

질문의 마지막 줄 ( 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()메소드를 통해 설정된 옵저버 내에서 사용자가 로그인 한 것을 감지하면 (예 : in if (authUser) {}) 콜렉션 uid에서이 사용자에 해당하는 고유 문서를 쿼리하는 데 사용합니다 users( 하나의 문서 읽기문서 참조 ). get()방법).


onAuthUserListener를 정의한 방식에 문제가 있습니까? 그런 다음 해당 방법에 대한 수정을 시도하면 authUser에서 사용자 모음으로 이동하려면 어떻게해야합니까?

"그래서 onAuthUserListener를 정의한 방식에 문제가 있습니까?"-> 내가 볼 수있는 것이 아닙니다. "authUser에서 사용자 컬렉션으로 이동하려면 어떻게해야합니까?" -> 올바르게 이해하면 컬렉션이 아닌 하나의 문서를 얻고 싶습니다. 답변의 코드가 작동해야합니다.
Renaud Tarnec

authUser를 받고 싶습니다. 내 시도에서 코드가 개선 되었습니까? authUser가 동일한 uid를 가진 사용자 컬렉션에 액세스 할 수있는 방법을 찾을 수 없습니다. 코드 제안을 이해하려고합니다. 첫 번째 단계로 내 코드가 어떻게 향상되는지. 어느 부분이 개선 / 수정인지 식별 ​​할 수 있습니까? 감사합니다
Mel

그렇게하면 어떻게 this.auth.onAuthStateChanged(authUser => { if (authUser) {console.log(authUser.uid) })됩니까?
Renaud Tarnec

모든 authUser 속성 (인증 수집 데이터)을 출력 할 수 있습니다. uid가 id 인 사용자 컬렉션의 관련 문서에서 사용자 데이터를 가져올 수 없습니다.
Mel

1

시험해보고 싶은 이론이 있습니다.

핸들러 next(authUser)내부에서 호출 할 때 onAuthStateChanged실행 중에 오류 (예 :)가 발생 한다고 생각합니다 cannot read property 'name' of undefined at ....

코드가 예상대로 작동하지 않는 이유는 호출하는 곳이 Promise 체인 next(authUser)내부에 있기 때문 then()입니다. 약속 내에서 발생하는 모든 오류가 발생하여 약속이 거부됩니다. 약속이 거부되면 오류가있는 첨부 된 오류 처리기를 호출합니다. 해당 Promise 체인에는 현재 이러한 오류 처리기가 없습니다.

내가 당신을 잃어버린 경우 , Promises crash course에 대한 이 블로그 게시물 을 읽은 다음 다시 오십시오.

그런 상황을 피하기 위해 어떻게해야합니까? 가장 간단한 방법은 Promise 핸들러 범위 next(authUser) 밖에서 호출하는 것 then()입니다. 우리는 이것을 사용하여 이것을 할 수 있습니다 window.setTimeout(function).

따라서 코드에서

next(authUser)

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

이는 Promise 체인에 걸리지 않고 정상적인 오류를 발생시킵니다.

중요한 것은 userDocRef.get()실패 했을 때 처리하는 catch 처리기가 없다는 것 입니다. 따라서 오류가 발생하면 코드가 대체 방법을 사용하도록 .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();
  }
}

감사합니다! 오늘 밤에 시도해 볼게요. 이 방법에 대해 배우게되어 기쁩니다. 피드백을 보내 드리겠습니다.

안녕 샘-이 제안을 해주셔서 감사합니다. 나는 당신이 링크 한 문서를 읽는 데 시간이 걸렸으며 이것에 대해 몇 가지 살펴 보았습니다. 도움을 주셔서 감사합니다. 그래도 문제가 해결되지 않았습니다. 사용자 컬렉션 속성에 액세스하려고하면 여전히 다음과 같은 오류가 발생합니다. TypeError : undefined의 'user'속성을 읽을 수 없음
Mel

@Mel 원래 코드를 실행할 때 TypeError에 대한 알림을 받았습니까? 당신이 그것을 언급 한 것은 이번이 처음입니다. 이는이 코드가 Promise의 범위를 벗어나는 오류를 발생시키는 것을 의미합니다. 당신은 출력을 제공 할 수 있습니까 console.log(snapshot.data())?
samthecodingman

나는이 시도 - 오류 메시지가 말한다 : 형식 오류 : snapshot.data는 함수가 아닙니다

나는 계속 움직일 것이다-아마 나는 이것을 좋은 지점에 기록하려고하지 않고있다
Mel

0

당신에 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개체에 공개 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));

계속 도와 주셔서 감사합니다 @samthecodingman. 나는 이것을 보았다. 내 목표는 authUser의 uid를 읽고 해당 사용자에 대한 관련 사용자 모음의 속성을 얻는 데 사용할 수 있도록하는 것입니다 (사용자 모음의 이름은 firebase 인증 모음의 displayName 이상입니다-그래서 시도하지 않습니다) 인증 테이블의 속성 읽기
Mel

리스너 componentDidMount 함수에 대한 제안 된 변경으로 오류가 발생하지 않았지만 작동하지 않았습니다. 이 리스너를 사용하여 대시 보드 구성 요소에 authUser 값을 기록하려고하면 authUser가 정의되지 않았다는 오류가 발생합니다. AuthContext.Provider에 componentDidMount를 정의한 방식으로 사용할 때이 오류가 발생하지 않습니다. 로그 메시지의 임의 속성에 대한 정보를 가져 주셔서 감사합니다.

@Mel Dashboard파일 의 마지막 줄 이 export default withAuthentication(Dashboard);(그리고 아님 withFirebase) 임을 확인할 수 있습니까?
samthecodingman

확인했습니다. withFirebase는 withAuthentication에 통합되어 있으므로 해당 HOC를 통해 선택됩니다.

@Mel Slack에서 보낸 메시지를 확인할 수 있습니까
samthecodingman

0

다른 사람이 비슷하게 붙어 있으면 여기에서 해결책을 찾았습니다 .Firebase & React : CollectionReference.doc () 인수 유형

페이지 새로 고침에서는 작동하지 않지만 (여전히 uid가 null이라는 오류가 발생 함) useEffect에 대한 후크는 componentDidMount 함수를 Mount 및 Update의 조합으로 바꿔야합니다. 다음에 시도하고 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.