Làm thế nào để bạn có được các chi tiết db người dùng liên quan đến một authUser trong Firestore?


10

Tôi đang cố gắng tìm ra cách lấy tên người dùng là thuộc tính được lưu trữ trong bộ sưu tập người dùng, đã được hợp nhất với các thuộc tính được tạo bởi mô hình xác thực firebase.

Tôi có thể truy cập authUser - cung cấp cho tôi các căn cứ hỏa lực giới hạn thu thập trong công cụ xác thực và sau đó tôi đang cố gắng chuyển từ đó đến bộ sưu tập người dùng liên quan (sử dụng cùng một uid).

Tôi có một người tiêu dùng bối cảnh phản ứng với:

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

Sau đó, trong thành phần của tôi, tôi đang cố gắng sử dụng:

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

Trong firebase.js của tôi - Tôi nghĩ rằng tôi đã cố gắng hợp nhất các thuộc tính authUser từ mô hình Xác thực với các thuộc tính bộ sưu tập người dùng như sau:

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

Tôi không thể tìm cách lấy từ authUser (hoạt động để đưa tôi đến các thuộc tính Xác thực) - đến bộ sưu tập người dùng có id với cùng một uid từ bộ sưu tập Xác thực.

Tôi đã thấy bài đăng này , dường như có cùng một vấn đề và đã cố gắng tìm ra câu trả lời được cho là gợi ý - nhưng dường như tôi không thể tìm ra cách nào để chuyển từ bộ sưu tập Xác thực sang bộ sưu tập người dùng và tôi không biết hợp nhất đang làm gì cho tôi nếu nó không cho tôi quyền truy cập vào các thuộc tính trên bộ sưu tập người dùng từ authUser.

Tôi đã cố gắng sử dụng một trình trợ giúp trong firebase.js để cung cấp cho tôi một người dùng từ một uid - nhưng điều đó dường như cũng không giúp được gì.

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

Nỗ lực tiếp theo

Để thêm nền, tôi đã tạo một thành phần thử nghiệm có thể đăng nhập (nhưng không kết xuất) authUser, như sau:

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;

Nhật ký hiển thị chi tiết về uid, email, v.v. trong nhật ký, nhưng nó nằm trong một danh sách dài các mục - nhiều mục được mở đầu bằng 1 hoặc 2 chữ cái (Tôi không thể tìm thấy khóa để tìm ra mỗi tiền tố này chữ có nghĩa). Ví dụ được trích xuất dưới đây:

nhập mô tả hình ảnh ở đây

CẬP NHẬT VỀ NHẬN XÉT NÀY:

Trước đây, tôi đã nói: Các trường cho uid, email, v.v. dường như không được lồng bên dưới các tiền tố này, nhưng nếu tôi cố gắng:

 console.log(authUser.email)

, Tôi nhận được một lỗi cho biết:

TypeError: Không thể đọc thuộc tính 'email' của null

Bản cập nhật: Tôi mới nhận ra rằng trong nhật ký giao diện điều khiển, tôi phải mở rộng menu thả xuống có nhãn:

Q {I: Mảng (0), l:

để xem thuộc tính email. Có ai biết những gì jibberish ám chỉ? Tôi không thể tìm thấy chìa khóa để tìm hiểu ý nghĩa của Q, tôi hoặc l, để biết liệu tôi có nên tham khảo những điều này để đến các thuộc tính có liên quan trong bảng Xác thực hay không. Có lẽ nếu tôi có thể tìm ra điều đó - tôi có thể tìm cách đến bộ sưu tập người dùng bằng cách sử dụng uid từ bộ sưu tập Xác thực.

Có ai đã sử dụng phản ứng ở mặt trước, với một người tiêu dùng bối cảnh để tìm ra người dùng hiện tại là ai chưa? Nếu vậy, làm thế nào để bạn truy cập các thuộc tính của chúng trên mô hình Xác thực và làm thế nào bạn truy cập các thuộc tính trên bộ sưu tập Người dùng liên quan (trong đó docId trên tài liệu Người dùng là uid từ bảng Xác thực)?

TIẾP THEO TIẾP THEO

Nỗ lực tiếp theo đã tạo ra một kết quả rất kỳ lạ.

Tôi có 2 trang riêng biệt là người tiêu dùng bối cảnh. Sự khác biệt giữa chúng là một cái là một hàm và cái kia là một thành phần lớp.

Trong thành phần chức năng, tôi có thể kết xuất {authUser.email}. Khi tôi cố gắng làm điều tương tự trong thành phần lớp, tôi gặp lỗi:

TypeError: Không thể đọc thuộc tính 'email' của null

Lỗi này đến từ cùng một phiên với cùng một người dùng đã đăng nhập.

Lưu ý: trong khi tài liệu về căn cứ hỏa lực nói rằng một thuộc tính hiện tại của Người dùng có sẵn trên auth - Tôi chưa thể làm cho nó hoạt động được.

Thành phần chức năng của tôi có:

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;

Mặc dù tôi không thể truy cập các thuộc tính bộ sưu tập Người dùng trong đó docId trên tài liệu người dùng giống với uid của người dùng được xác thực, từ thành phần này, tôi có thể xuất thuộc tính email trên bộ sưu tập auth cho người dùng này.

Mặc dù tài liệu Firebase cung cấp lời khuyên này để quản lý người dùng và truy cập các thuộc tính ở đây, tôi chưa tìm thấy cách nào để thực hiện phương pháp này trong phản ứng. Mỗi biến thể của một nỗ lực thực hiện việc này, cả bằng cách tạo các trình trợ giúp trong firebase.js của tôi và bằng cách cố gắng bắt đầu từ đầu trong một thành phần đều tạo ra lỗi khi truy cập firebase. Tuy nhiên tôi có thể tạo danh sách người dùng và thông tin bộ sưu tập Người dùng liên quan của họ (Tôi không thể có được người dùng dựa trên người dùng authUser).

Thành phần lớp học của tôi có:

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;

Trong AuthContext.Provider của tôi - Tôi có:

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;

TIẾP THEO TIẾP THEO

Điều thực sự kỳ lạ là với nỗ lực này, tôi đang cố gắng điều khiển ghi nhật ký các giá trị mà tôi có thể thấy tồn tại trong cơ sở dữ liệu và giá trị của tên được trả về là 'không xác định' trong đó db có một chuỗi trong đó.

Nỗ lực này có:

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

TIẾP THEO TIẾP THEO

Vì vậy, nỗ lực này là vụng về và không sử dụng các trợ giúp hoặc các truy vấn chụp nhanh mà tôi đã cố gắng sử dụng ở trên, nhưng đăng nhập các thuộc tính tài liệu bộ sưu tập người dùng vào bảng điều khiển như sau:

{this.props.firebase.db.collection ('người dùng'). doc (authUser.uid) .get ()

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

    } 

Những gì tôi không thể làm là tìm cách hiển thị tên đó trong jsx

Làm thế nào để bạn thực sự in đầu ra?

Khi tôi cố gắng:

{ 


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

                }

Tôi nhận được một lỗi cho biết:

LoạiError: this.props.firebase.db.collection (...). Doc (...). Get (...). Dữ liệu không phải là một hàm

Khi tôi cố gắng:

{ 



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

Tôi nhận được một lỗi cho biết:

Dòng 281: 23: Dự kiến ​​một lệnh gán hoặc gọi hàm và thay vào đó thấy một biểu thức không có biểu thức không sử dụng

Khi tôi cố gắng:

{ 


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

Thông báo lỗi cho biết:

Dự kiến ​​một lệnh gọi hoặc hàm và thay vào đó đã thấy một biểu thức

Tôi đã sẵn sàng từ bỏ việc cố gắng tìm hiểu làm thế nào để các truy vấn chụp nhanh hoạt động - nếu tôi có thể lấy tên của bộ sưu tập người dùng để hiển thị trên màn hình. Bất cứ ai có thể giúp với bước đó?

TIẾP THEO TIẾP THEO

Tôi tìm thấy bài này . Nó có một lời giải thích tốt về những gì cần phải xảy ra, nhưng tôi không thể thực hiện nó như được hiển thị vì thành phầnDidMount không biết authUser là gì.

Nỗ lực hiện tại của tôi là như sau - tuy nhiên, như được viết hiện tại, authUser là một trình bao bọc trên giá trị trả về - và phân đoạn thành phầnDidMount không biết authUser là gì.

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

TIẾP THEO TIẾP THEO

Tiếp theo, tôi đã thử gói tuyến đường cho bảng điều khiển bên trong AuthContext.Consumer để toàn bộ thành phần có thể sử dụng nó - do đó cho phép tôi truy cập người dùng đã đăng nhập trong hàm thành phầnDidMount.

Tôi đã thay đổi tuyến đường thành:

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

và loại bỏ người tiêu dùng khỏi câu lệnh kết xuất thành phần bảng điều khiển.

Sau đó, trong thành phầnDidMount trên thành phần Bảng điều khiển, tôi đã thử:

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

Khi tôi thử điều này, tôi nhận được một lỗi cho biết:

FirebaseError: Function CollectionReference.doc () yêu cầu đối số đầu tiên của nó phải là kiểu chuỗi không trống, nhưng đó là: một đối tượng DocumentReference tùy chỉnh

ATTEMPT TIẾP THEO Những người dưới đây dường như tìm thấy điều gì đó hữu ích trong giải pháp đề xuất đầu tiên. Tôi không thể tìm thấy bất cứ điều gì hữu ích trong đó, nhưng đọc lại các đề xuất của nó, tôi đang cố gắng xem ví dụ trong tài liệu về căn cứ hỏa lực (nó không tiết lộ cách đưa ra giá trị: uid cho yêu cầu .doc () ), như sau:

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!");
      }

về cơ bản là khác với nỗ lực của tôi trong hàm thành phầnDidMount, đó là:

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

    );  
  }

Có lẽ giải quyết bước đó là một đầu mối sẽ giúp đưa điều này đến một kết quả. Bất cứ ai cũng có thể tìm thấy bất kỳ tài liệu Firestore tốt hơn để hiển thị làm thế nào để có được một bản ghi bộ sưu tập người dùng bằng cách sử dụng một người nghe đã đăng nhập uid?

Cuối cùng, tôi có thể thấy từ ví dụ trong phòng thí nghiệm mã FriendlyEats , rằng có một nỗ lực để cung cấp doc.id cho giá trị tìm kiếm id trong mã. Tôi không biết mã này được viết bằng ngôn ngữ nào - nhưng nó trông giống với ngôn ngữ mà tôi đang cố gắng thực hiện - Tôi chỉ không thể thấy làm thế nào để chuyển từ ví dụ đó sang thứ mà tôi biết cách làm việc.

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

FYI thuật ngữ của bạn không hoàn toàn đúng, và làm cho câu hỏi này khó đọc. Không có gì trong Firebase được gọi là "bảng". Trong Firebase Auth, chỉ có người dùng - không có "Bảng xác thực". Trong Firestore, có các bộ sưu tập và tài liệu trong các bộ sưu tập đó, nhưng không có bảng. Tôi đang cố gắng tìm ra nơi bạn đang bị mắc kẹt và cách mã bạn đã hiển thị không hoạt động theo cách bạn mong đợi, nhưng tôi chỉ không ghép nó lại với nhau. Cân nhắc chỉnh sửa câu hỏi để sử dụng thuật ngữ chuẩn hơn mà bạn tìm thấy các tài liệu và rõ ràng hơn về những gì không hoạt động như bạn mong đợi.
Doug Stevenson

Fine - hạnh phúc để thay thế các bảng cho bộ sưu tập. Điểm vẫn giống nhau.
Mel

Quan điểm chính của tôi là tôi không thể thực sự hiểu ý của bạn và thuật ngữ này không giúp được gì. Bạn có thể giải thích chi tiết hơn những gì không hoạt động trong mã mà bạn đã hiển thị? Đã làm một cái gì đó không làm việc như mong đợi? Bất kỳ lỗi hoặc nhật ký gỡ lỗi để minh họa?
Doug Stevenson

Không có gì hoạt động. Tôi hy vọng sẽ tìm được cách truy cập chi tiết bộ sưu tập người dùng từ trình nghe authUser. authUser được định nghĩa trong trình xử lý ngữ cảnh và phương thức lớp liên quan lắng nghe các thay đổi trên phương thức. Tôi không thể vượt qua các thuộc tính trong bộ sưu tập xác thực - Tôi đang cố gắng truy cập vào bộ sưu tập người dùng có liên quan ở Firestore. Không có nhật ký. Chỉ cần thông báo lỗi cho biết trường không xác định.
Mel

1
Tôi đề nghị bắt đầu với một nhiệm vụ đơn giản, làm cho nó hoạt động để có được một số kinh nghiệm, sau đó áp dụng điều đó cho các vấn đề phức tạp hơn như vấn đề bạn có bây giờ. Về cơ bản không có gì sai về tài liệu (tôi biết, vì tôi sử dụng nó mọi lúc và tôi giúp mọi người như vậy). Để được trợ giúp về Stack Overflow, bạn sẽ cần phải minh họa một vấn đề cụ thể, lý tưởng nhất là MCVE mà bất kỳ ai cũng có thể sử dụng để tái tạo vấn đề. Chỉ cần nói "Tôi không thể làm cho nó hoạt động" là không đủ. stackoverflow.com/help/minimal-reproducible-example
Doug Stevenson

Câu trả lời:


5

Tôi hiểu từ dòng cuối cùng của câu hỏi của bạn ( users = () => this.db.collection('users');) rằng bộ sưu tập nơi bạn lưu trữ thông tin bổ sung về người dùng được gọi usersvà tài liệu người dùng trong bộ sưu tập này sử dụng userId (uid) làm docId.

Sau đây nên thực hiện các mẹo (chưa được kiểm tra):

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

Vì vậy, trong trình quan sát được thiết lập thông qua onAuthStateChanged()phương thức, khi chúng tôi phát hiện ra rằng người dùng đã đăng nhập (tức là if (authUser) {}), chúng tôi sử dụng nó uidđể truy vấn tài liệu duy nhất tương ứng với người dùng này trong usersbộ sưu tập (xem đọc một tài liệu và tài liệu của get()phương pháp).


Vậy có gì sai với cách tôi đã định nghĩa trênAuthUserListener? Sau đó, nếu tôi thử các sửa đổi của bạn cho phương pháp đó, tôi phải làm gì để có được bộ sưu tập người dùng từ authUser?
Mel

"Vì vậy, có gì đó không đúng với cách tôi đã xác định trênAuthUserListener?" -> không phải từ những gì tôi có thể thấy. "Tôi phải làm gì để có được bộ sưu tập người dùng từ authUser?" -> Nếu tôi hiểu chính xác, bạn muốn lấy MỘT tài liệu, không phải bộ sưu tập. Các mã trong câu trả lời nên làm việc.
Renaud Tarnec

Tôi muốn nhận authUser - mã của bạn có phải là một cải tiến trong nỗ lực của tôi không? Tôi không thể tìm ra cách hoạt động để có authUser cấp cho tôi quyền truy cập vào bộ sưu tập người dùng có cùng uid. Tôi đang cố gắng để hiểu đề xuất mã của bạn - về cách cải thiện điều đó đối với tôi như là bước đầu tiên. Xin vui lòng bạn có thể xác định bit nào của nó là cải thiện / chỉnh sửa? Cảm ơn bạn
Mel

Điều gì xảy ra nếu bạn làm gì this.auth.onAuthStateChanged(authUser => { if (authUser) {console.log(authUser.uid) })?
Renaud Tarnec

Tôi có thể xuất tất cả các thuộc tính authUser (dữ liệu thu thập xác thực). Tôi không thể lấy dữ liệu người dùng từ tài liệu liên quan trong bộ sưu tập người dùng có uid là id
Mel

1

Tôi có một lý thuyết mà tôi muốn bạn kiểm tra.

Tôi nghĩ rằng khi bạn đang gọi next(authUser)bên trong onAuthStateChangedtrình xử lý của mình , trong khi thực thi, nó gặp lỗi (chẳng hạn như cannot read property 'name' of undefined at ...).

Lý do mã của bạn không hoạt động như mong đợi là vì nơi bạn gọi next(authUser) , nó nằm trong then()chuỗi Promise. Bất kỳ lỗi nào được ném bên trong Promise sẽ bị bắt và khiến Promise bị từ chối. Khi một Promise bị từ chối, nó sẽ gọi nó là các trình xử lý lỗi được đính kèm với lỗi. Chuỗi Promise đang được đề cập hiện không có bất kỳ trình xử lý lỗi nào như vậy.

Nếu tôi mất bạn, hãy đọc bài đăng trên blog này cho khóa học sụp đổ của Promise và sau đó quay lại.

Vậy chúng ta có thể làm gì để tránh tình trạng như vậy? Đơn giản nhất sẽ là gọi next(authUser) bên ngoàithen() phạm vi của trình xử lý Promise . Chúng ta có thể làm điều này bằng cách sử dụngwindow.setTimeout(function) .

Vì vậy, trong mã của bạn, bạn sẽ thay thế

next(authUser)

với

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

Điều này sẽ đưa ra bất kỳ lỗi nào như bình thường thay vì bị bắt bởi chuỗi Promise.

Điều quan trọng, bạn chưa có một trình xử lý bắt xử lý khi userDocRef.get()thất bại. Vì vậy, chỉ cần thêm .catch(() => setTimeout(fallback))vào cuốithen() để mã của bạn sử dụng phương thức dự phòng nếu nó bị lỗi.

Vì vậy, chúng tôi kết thúc với:

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

Mã chỉnh sửa

Giải thích trên sẽ cho phép bạn sửa mã của mình nhưng đây là phiên bản của tôi Firebase lớp học với nhiều thay đổi dễ sử dụng khác nhau.

Sử dụng:

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
}

Định nghĩa lớp:

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

Lưu ý rằng trong phiên bản này, onUserDataListenerphương thức trả về chức năng hủy đăng ký onAuthStateChanged. Khi thành phần của bạn không được kết nối, bạn nên tách bất kỳ trình nghe có liên quan nào để không bị rò rỉ bộ nhớ hoặc bị hỏng mã chạy trong nền khi không cần thiết.

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

Cảm ơn bạn! Tôi sẽ thử tối nay. Tôi rất hào hứng khi tìm hiểu về cách này. Tôi sẽ quay lại sớm với thông tin phản hồi.
Mel

Xin chào Sam - cảm ơn bạn đã đưa ra đề nghị này. Tôi đã dành một chút thời gian để đọc qua các tài liệu bạn liên kết và tôi đã có một vài điều về việc này. Trong khi tôi biết ơn sự giúp đỡ - điều này đã không giải quyết được vấn đề của tôi. Khi tôi cố gắng truy cập các thuộc tính bộ sưu tập người dùng, tôi vẫn gặp lỗi thông báo: TypeError: Không thể đọc thuộc tính 'người dùng' không xác định
Mel

@Mel Khi bạn chạy mã gốc, bạn có được thông báo về TypeError không? Đây là lần đầu tiên bạn đề cập đến nó. Điều đó có nghĩa là mã này đã thực hiện công việc đưa lỗi ra ngoài phạm vi của Promise. Bạn có thể cung cấp đầu ra của console.log(snapshot.data())?
samthecodingman

Tôi đã thử điều này - thông báo lỗi cho biết: TypeError: snapshot.data không phải là một chức năng
Mel

Tôi sẽ tiếp tục cố gắng di chuyển nó xung quanh - có thể tôi không cố gắng đăng nhập vào vị trí tốt
Mel

0

Khi AuthContext.Providertriển khai, bạn truy cập onAuthStateChanged trực tiếp vào trình nghe của SDK :

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

Điều này nên được thay đổi để sử dụng onAuthUserListenertừ lớp người trợ giúp của bạn:

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

Về các thông điệp tường trình chứa rất nhiều thuộc tính ngẫu nhiên, điều này là do firebase.Userđối tượng có cả API công khaitriển khai với một số thuộc tính và phương thức riêng được rút gọn khi biên dịch. Vì các thuộc tính và phương thức rút gọn này không được đánh dấu rõ ràng là không thể đếm được, nên chúng được bao gồm trong bất kỳ đầu ra nhật ký nào. Thay vào đó, nếu bạn chỉ muốn ghi nhật ký các phần thực sự hữu ích, bạn có thể hủy cấu trúc và cấu trúc lại đối tượng bằng cách sử dụng:

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

Cảm ơn @samthecodingman đã tiếp tục cố gắng giúp tôi. Tôi đã có một cái nhìn về điều này. Mục tiêu của tôi là đọc uid của authUser để tôi có thể sử dụng nó để lấy các thuộc tính của bộ sưu tập người dùng liên quan cho người dùng đó (tên trong bộ sưu tập người dùng có nhiều hơn displayName trong bộ sưu tập auth firebase - vì vậy tôi không cố gắng đọc thuộc tính của bảng Xác thực.
Mel

Thay đổi được đề xuất cho hàm listDidMount của trình nghe không gây ra lỗi - nhưng nó không hoạt động. Khi nó cố gắng ghi lại giá trị của authUser trong thành phần bảng điều khiển bằng trình nghe này - tôi gặp lỗi khi nói authUser không được xác định. Tôi không gặp phải lỗi này khi tôi sử dụng thành phầnDidMount cho AuthContext.Provider theo cách tôi đã xác định. Cảm ơn thông tin về các thuộc tính ngẫu nhiên trong các thông điệp tường trình.
Mel

@Mel Bạn có thể xác nhận rằng dòng cuối cùng của Dashboardtệp của bạn là export default withAuthentication(Dashboard);(và không withFirebase)
samthecodingman

Đã xác nhận. withFirebase được kết hợp với withAuthentication - vì vậy nó được chọn thông qua HOC đó.
Mel

@Mel Bạn có thể kiểm tra tin nhắn tôi đã gửi cho bạn trên Slack
samthecodingman

0

Nếu có ai khác bị mắc kẹt tương tự, tôi đã tìm thấy giải pháp ở đây: Kiểu đối số Firebase & React: CollectionReference.doc ()

Nó không hoạt động khi làm mới trang (vẫn đưa ra lỗi là uid là null) nhưng phản ứng hook với useEffect nên thay thế hàm thành phầnDidMount bằng kết hợp Mount và Update. Tôi đang cố gắng tiếp theo.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.