Bagaimana Anda mendapatkan detail db pengguna yang terkait dengan authUser di Firestore?

10

Saya mencoba mencari cara untuk mendapatkan nama pengguna yang merupakan atribut yang disimpan dalam koleksi pengguna, yang telah digabungkan dengan atribut yang dibuat oleh model otentikasi firebase.

Saya dapat mengakses authUser - yang memberi saya bidang terbatas yang dikumpulkan firebase di alat otentikasi, dan kemudian saya mencoba untuk mendapatkan dari sana ke koleksi pengguna terkait (yang menggunakan uid yang sama).

Saya memiliki konsumen konteks reaksi dengan:

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

Kemudian di komponen saya, saya mencoba untuk menggunakan:

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

Di firebase.js saya - saya pikir saya sudah mencoba untuk menggabungkan atribut authUser dari model Otentikasi dengan atribut kumpulan pengguna sebagai berikut:

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

Saya tidak dapat menemukan cara untuk mendapatkan dari authUser (yang berfungsi untuk membawa saya ke atribut Otentikasi) - ke koleksi pengguna yang memiliki id dengan cairan yang sama dari koleksi Otentikasi.

Saya telah melihat posting ini , yang tampaknya memiliki masalah yang sama dan mencoba mencari tahu apa jawaban yang seharusnya mengisyaratkan - tapi sepertinya saya tidak dapat menemukan cara yang berfungsi untuk mendapatkan dari koleksi Otentikasi ke koleksi pengguna dan saya tidak tahu apa yang dilakukan gabungan untuk saya jika itu tidak memberi saya akses ke atribut pada kumpulan pengguna dari authUser.

Saya mencoba menggunakan helper di firebase.js untuk memberi saya pengguna dari uid - tapi itu sepertinya juga tidak membantu.

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

Upaya selanjutnya

Untuk menambahkan lebih banyak latar belakang, saya telah membuat komponen uji yang dapat mencatat (tetapi tidak membuat) authUser, sebagai berikut:

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;

Log menunjukkan detail untuk uid, email, dll di log, tetapi ada di antara daftar item yang panjang - banyak di antaranya diawali dengan 1 atau 2 huruf (Saya tidak dapat menemukan kunci untuk mengetahui apa yang masing-masing awalan ini. berarti huruf). Contoh diekstrak di bawah ini:

masukkan deskripsi gambar di sini

MEMPERBARUI KOMENTAR INI:

Sebelumnya, saya berkata: Bidang untuk uid, email, dll tampaknya tidak bersarang di bawah awalan ini, tetapi jika saya mencoba untuk:

 console.log(authUser.email)

, Saya mendapatkan kesalahan yang mengatakan:

TypeError: Tidak dapat membaca properti 'email' dari nol

Pembaruan: Saya baru menyadari bahwa di log konsol, saya harus membuka menu tarik-turun yang berlabel:

Q {I: Array (0), l:

untuk melihat atribut email. Adakah yang tahu apa yang disinggung oleh omong kosong ini? Saya tidak dapat menemukan kunci untuk mencari tahu apa arti Q, I atau l, untuk mengetahui apakah saya seharusnya merujuk hal-hal ini untuk mendapatkan atribut yang relevan di tabel Otentikasi. Mungkin jika saya dapat mengetahuinya - saya dapat menemukan cara untuk mendapatkan koleksi pengguna menggunakan uid dari koleksi Otentikasi.

Adakah yang pernah menggunakan reaksi di ujung depan, dengan konsumen konteks untuk mencari tahu siapa pengguna saat ini? Jika demikian, bagaimana Anda mengakses atribut mereka pada model Otentikasi dan bagaimana Anda mengakses atribut pada koleksi Pengguna terkait (di mana dokumen pada dokumen Pengguna adalah cairan dari tabel Otentikasi)?

PERIKSAAN BERIKUTNYA

Upaya selanjutnya telah menghasilkan hasil yang sangat aneh.

Saya memiliki 2 halaman terpisah yang merupakan konteks konsumen. Perbedaan di antara mereka adalah bahwa satu adalah fungsi dan yang lainnya adalah komponen kelas.

Di komponen fungsi, saya bisa me-render {authUser.email}. Ketika saya mencoba melakukan hal yang sama di komponen kelas, saya mendapatkan kesalahan yang mengatakan:

TypeError: Tidak dapat membaca properti 'email' dari nol

Kesalahan ini berasal dari sesi yang sama dengan pengguna login yang sama.

Catatan: sementara dokumentasi firebase mengatakan bahwa properti User saat ini tersedia di auth - saya belum bisa membuatnya berfungsi sama sekali.

Komponen fungsi saya memiliki:

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;

Meskipun saya tidak bisa mendapatkan atribut kumpulan pengguna di mana docId pada dokumen pengguna sama dengan uid dari pengguna yang diautentikasi, dari komponen ini, saya bisa menampilkan atribut email pada koleksi auth untuk pengguna ini.

Sedangkan dokumentasi Firebase memberikan saran ini untuk mengelola pengguna dan mengakses atribut di sini, saya belum menemukan cara untuk mengimplementasikan pendekatan ini sebagai reaksi. Setiap variasi dari upaya melakukan hal ini, baik dengan membuat helper di firebase.js saya dan dengan mencoba memulai dari awal pada suatu komponen menghasilkan kesalahan dalam mengakses firebase. Namun saya dapat menghasilkan daftar pengguna dan informasi pengumpulan Pengguna terkait (saya tidak bisa mendapatkan pengguna berdasarkan siapa pengguna authUser).

Komponen kelas saya memiliki:

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;

Dalam AuthContext.Provider saya - Saya punya:

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;

PERIKSAAN BERIKUTNYA

Sangat aneh bahwa dengan upaya ini, saya mencoba untuk menghibur log nilai-nilai yang dapat saya lihat ada di database dan nilai nama dikembalikan sebagai 'tidak terdefinisi' di mana db memiliki string di dalamnya.

Upaya ini memiliki:

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

PERIKSAAN BERIKUTNYA

Jadi upaya ini kikuk dan tidak menggunakan bantuan atau kueri snapshot yang saya coba gunakan di atas, tetapi catat atribut kumpulan dokumen pengguna ke konsol sebagai berikut:

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

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

    } 

Yang tidak bisa saya lakukan adalah menemukan cara untuk merender nama itu di jsx

Bagaimana Anda benar-benar mencetak hasilnya?

Ketika saya mencoba:

{ 


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

                }

Saya mendapatkan kesalahan yang mengatakan:

TypeError: this.props.firebase.db.collection (...). Doc (...). Get (...). Data bukan fungsi

Ketika saya mencoba:

{ 



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

Saya mendapatkan kesalahan yang mengatakan:

Baris 281: 23: Diharapkan penugasan atau panggilan fungsi dan bukannya melihat ekspresi tanpa-ekspresi yang tidak digunakan

Ketika saya mencoba:

{ 


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

Pesan kesalahan mengatakan:

Diharapkan panggilan tugas atau fungsi dan bukannya melihat ekspresi

Saya siap untuk menyerah untuk mencari tahu cara mendapatkan permintaan snapshot agar berfungsi - jika saya bisa mendapatkan nama koleksi pengguna untuk ditampilkan di layar. Adakah yang bisa membantu dengan langkah itu?

PERIKSAAN BERIKUTNYA

Saya menemukan posting ini . Ini memiliki penjelasan yang baik tentang apa yang perlu terjadi, tetapi saya tidak dapat mengimplementasikannya seperti yang ditunjukkan karena componentDidMount tidak tahu apa itu authUser.

Upaya saya saat ini adalah sebagai berikut - namun, seperti yang saat ini ditulis, authUser adalah pembungkus nilai pengembalian - dan segmen componentDidMount tidak tahu apa itu 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);

PERIKSAAN BERIKUTNYA

Selanjutnya, saya mencoba membungkus rute untuk dashboard di dalam AuthContext.Consumer sehingga seluruh komponen dapat menggunakannya - dengan demikian membiarkan saya mengakses pengguna yang masuk dalam fungsi componentDidMount.

Saya mengubah rute ke:

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

dan menghapus konsumen dari pernyataan render komponen dasbor.

Kemudian di componentDidMount pada komponen Dashboard, saya mencoba:

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

Ketika saya mencoba ini, saya mendapatkan kesalahan yang mengatakan:

FirebaseError: Function CollectionReference.doc () membutuhkan argumen pertamanya menjadi tipe string yang tidak kosong, tapi itu adalah: objek DocumentReference kustom

PERCOBAAN BERIKUTNYA Orang-orang di bawah ini sepertinya menemukan sesuatu yang bermanfaat dalam solusi yang diusulkan pertama. Saya belum dapat menemukan sesuatu yang berguna di dalamnya, tetapi membaca kembali saran-sarannya, saya berjuang untuk melihat bagaimana contoh dalam dokumentasi firebase (tidak mengungkapkan bagaimana memberikan: nilai uid ke permintaan .doc () ), yaitu sebagai berikut:

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

secara fundamental berbeda dengan upaya saya dalam fungsi componentDidMount, yaitu:

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

    );  
  }

Mungkin memecahkan langkah itu adalah petunjuk yang akan membantu untuk menggerakkan ini menuju hasil. Adakah yang bisa menemukan dokumentasi firestore yang lebih baik untuk menunjukkan cara mendapatkan catatan koleksi pengguna menggunakan pendengar pengguna yang masuk?

Untuk itu, saya dapat melihat dari contoh lab kode FriendlyEats , bahwa ada upaya untuk memberikan doc.id ke nilai pencarian id dalam kode. Saya tidak tahu bahasa apa kode ini ditulis - tetapi memang terlihat mirip dengan apa yang saya coba lakukan - saya hanya tidak bisa melihat bagaimana cara beralih dari contoh itu ke sesuatu yang saya tahu cara kerjanya.

display: function(doc) {
      var data = doc.data();
      data['.id'] = doc.id;
      data['go_to_restaurant'] = function() {
        that.router.navigate('/restaurants/' + doc.id);
      };
Mel
sumber
FYI terminologi Anda kurang tepat, dan membuat pertanyaan ini sulit dibaca. Tidak ada apa pun di Firebase yang disebut "tabel". Di Firebase Auth, hanya ada pengguna - tidak ada "tabel Otentikasi". Di Firestore, ada koleksi, dan dokumen di dalam koleksi itu, tetapi tidak ada tabel. Saya mencoba mencari tahu di mana Anda terjebak dan bagaimana kode yang Anda tampilkan tidak bekerja seperti yang Anda harapkan, tapi saya hanya tidak menyatukannya. Pertimbangkan untuk mengedit pertanyaan agar menggunakan lebih banyak terminologi standar yang akan Anda temukan di dokumen, dan lebih jelas tentang apa yang tidak berfungsi seperti yang Anda harapkan.
Doug Stevenson
Baik - senang mengganti tabel untuk koleksi. Intinya masih sama.
Mel
Poin utama saya adalah bahwa saya tidak bisa benar-benar memahami maksud Anda, dan terminologinya tidak membantu. Bisakah Anda menjelaskan lebih detail apa yang tidak berfungsi dalam kode yang Anda perlihatkan? Apakah sesuatu tidak bekerja seperti yang diharapkan? Adakah kesalahan atau log debug untuk diilustrasikan?
Doug Stevenson
Tidak ada yang berhasil. Saya berharap dapat menemukan cara untuk mengakses detail koleksi pengguna dari pendengar authUser. authUser didefinisikan dalam penangan konteks dan metode kelas terkait yang mendengarkan perubahan pada metode. Saya tidak bisa melewati atribut dalam koleksi otentikasi - Saya mencoba untuk mendapatkan akses ke koleksi pengguna terkait di firestore. Tidak ada log. Hanya pesan kesalahan yang mengatakan bahwa bidang tersebut tidak ditentukan.
Mel
1
Saya sarankan mulai dengan tugas sederhana, mulai bekerja agar mendapat pengalaman, lalu terapkan itu untuk masalah yang lebih kompleks seperti yang Anda miliki sekarang. Tidak ada yang salah secara mendasar tentang dokumentasi (saya tahu, karena saya menggunakannya sepanjang waktu, dan saya membantu orang dengan hal yang sama). Untuk mendapatkan bantuan pada Stack Overflow, Anda harus mengilustrasikan masalah tertentu, idealnya MCVE yang dapat digunakan siapa saja untuk mereproduksi masalah. Mengatakan "Saya tidak bisa menjalankannya" tidak cukup. stackoverflow.com/help/minimal-reproducible-example
Doug Stevenson

Jawaban:

5

Saya mengerti dari baris terakhir pertanyaan Anda ( users = () => this.db.collection('users');) bahwa koleksi tempat Anda menyimpan info tambahan tentang pengguna dipanggil usersdan bahwa dokumen pengguna dalam koleksi ini menggunakan userId (uid) sebagai docId.

Berikut ini harus melakukan trik (belum diuji):

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

Jadi, dalam pengamat yang ditetapkan melalui onAuthStateChanged()metode, ketika kami mendeteksi bahwa pengguna masuk (yaitu di if (authUser) {}), kami menggunakannya uiduntuk meminta dokumen unik yang sesuai dengan pengguna ini dalam userskoleksi (lihat membaca satu dokumen , dan dokumen dari get()metode).

Renaud Tarnec
sumber
Jadi, apakah ada yang salah dengan cara saya mendefinisikan onAuthUserListener? Lalu jika saya mencoba amandemen Anda untuk metode itu, apa yang harus saya lakukan untuk mendapatkan koleksi pengguna dari authUser?
Mel
"Jadi, apakah ada yang salah dengan cara saya mendefinisikan onAuthUserListener?" -> bukan dari apa yang saya lihat. "Apa yang harus saya lakukan untuk mendapatkan koleksi pengguna dari authUser?" -> Jika saya mengerti benar Anda ingin mendapatkan SATU dokumen, bukan koleksi. Kode dalam jawaban harus berfungsi.
Renaud Tarnec
Saya ingin mendapatkan authUser - apakah kode Anda merupakan peningkatan pada upaya saya? Saya tidak dapat menemukan cara yang berfungsi untuk mendapatkan authUser untuk memberi saya akses ke koleksi pengguna yang memiliki uid yang sama. Saya mencoba memahami saran kode Anda - untuk bagaimana itu meningkatkan milik saya sebagai langkah pertama. Tolong bisakah Anda mengidentifikasi bagian mana yang merupakan peningkatan / koreksi? Terima kasih
Mel
Apa yang terjadi jika Anda melakukannya this.auth.onAuthStateChanged(authUser => { if (authUser) {console.log(authUser.uid) })?
Renaud Tarnec
Saya dapat menampilkan semua properti authUser (data pengumpulan otentikasi). Saya tidak bisa mendapatkan data pengguna dari dokumen terkait dalam koleksi pengguna yang memiliki id sebagai id
Mel
1

Saya punya teori yang ingin saya uji.

Saya pikir ketika Anda menelepon next(authUser)di dalam onAuthStateChangedhandler Anda , selama eksekusi ia menemukan kesalahan (seperti cannot read property 'name' of undefined at ...).

Alasan kode Anda tidak berfungsi seperti yang diharapkan adalah karena tempat Anda menelepon next(authUser), kode itu berada di dalam then()rantai Janji. Kesalahan apa pun yang dilemparkan ke dalam Janji akan ditangkap dan menyebabkan Janji tersebut ditolak. Ketika suatu Janji ditolak, itu akan menyebut itu penangan kesalahan terlampir dengan kesalahan. Rantai Janji yang dipermasalahkan saat ini tidak memiliki penangan kesalahan seperti itu.

Jika saya kehilangan Anda, baca posting blog ini untuk kursus kilat Promises lalu kembali.

Jadi apa yang bisa kita lakukan untuk menghindari situasi seperti itu? Yang paling sederhana adalah menelepon di next(authUser) luarthen() ruang lingkup pawang Promise . Kita bisa melakukan ini menggunakan window.setTimeout(function).

Jadi dalam kode Anda, Anda akan menggantinya

next(authUser)

dengan

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

Ini akan membuang kesalahan seperti biasa daripada tertangkap oleh rantai Janji.

Yang penting, Anda tidak memiliki penangan yang menangani ketika userDocRef.get()gagal. Jadi tambahkan saja .catch(() => setTimeout(fallback))di akhir then()sehingga kode Anda menggunakan metode fallback jika kesalahan keluar.

Jadi kita berakhir dengan:

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

Kode yang diedit

Penjelasan di atas seharusnya memungkinkan Anda untuk memperbaiki kode Anda, tetapi ini adalah versi Firebasekelas saya dengan berbagai perubahan kemudahan penggunaan.

Pemakaian:

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
}

Definisi kelas:

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

Perhatikan bahwa dalam versi ini, onUserDataListenermetode mengembalikan fungsi berhenti berlangganan onAuthStateChanged. Ketika komponen Anda dilepas, Anda harus melepaskan pendengar yang relevan sehingga Anda tidak mendapatkan kebocoran memori atau kode yang rusak berjalan di latar belakang ketika itu tidak diperlukan.

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();
  }
}
samthecodingman
sumber
Terima kasih! Saya akan coba malam ini. Saya senang belajar tentang cara ini. Saya akan segera kembali dengan umpan balik.
Mel
Hai Sam - terima kasih telah menawarkan saran ini. Saya meluangkan waktu untuk membaca dokumentasi yang Anda tautkan dan saya sudah membahas beberapa hal. Meskipun saya bersyukur atas bantuannya - ini tidak menyelesaikan masalah saya. Ketika saya mencoba mengakses atribut kumpulan pengguna, saya masih mendapatkan kesalahan yang mengatakan: TypeError: Tidak dapat membaca properti 'pengguna' yang tidak terdefinisi
Mel
@Mel Ketika Anda menjalankan kode asli, apakah Anda diberitahu tentang TypeError? Ini adalah pertama kalinya Anda menyebutkannya. Yang berarti kode ini melakukan tugasnya membuang kesalahan di luar lingkup Janji. Bisakah Anda memberikan output console.log(snapshot.data())?
samthecodingman
Saya mencoba ini - pesan kesalahan mengatakan: TypeError: snapshot.data bukan fungsi
Mel
Saya akan terus mencoba untuk memindahkannya - mungkin saya tidak mencoba mencatat ini di tempat yang bagus
Mel
0

Dalam AuthContext.Providerimplementasi Anda, Anda mengakses onAuthStateChanged pendengar SDK secara langsung:

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

Ini harus diubah untuk menggunakan onAuthUserListenerdari kelas pembantu Anda:

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

Mengenai pesan log yang diisi dengan banyak properti acak, ini karena firebase.Userobjek memiliki API publik dan implementasi dengan sejumlah properti pribadi dan metode yang diperkecil saat dikompilasi. Karena properti dan metode yang diperkecil ini tidak secara eksplisit ditandai sebagai tidak dapat dihitung, mereka dimasukkan dalam output log apa pun. Jika Anda ingin hanya mencatat bagian yang benar-benar berguna, Anda dapat merusak dan merestrukturisasi objek menggunakan:

// 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
sumber
Terima kasih @samthecodingman untuk terus mencoba membantu saya. Saya telah melihat ini. Tujuan saya adalah untuk membaca uid dari authUser sehingga saya bisa menggunakannya untuk mendapatkan atribut dari kumpulan pengguna terkait untuk pengguna itu (nama dalam koleksi pengguna memiliki lebih dari tampilanName di kumpulan authebaseb Firebase - jadi saya tidak mencoba untuk baca properti dari tabel Authentication
Mel
Perubahan yang disarankan ke fungsi componentDidMount listener tidak menimbulkan kesalahan - tetapi tidak berhasil. Ketika mencoba untuk mencatat nilai authUser di komponen dasbor menggunakan pendengar ini - saya mendapatkan kesalahan mengatakan authUser tidak didefinisikan. Saya tidak mendapatkan kesalahan ini ketika saya menggunakan componentDidMount untuk AuthContext.Provider dengan cara saya mendefinisikannya. Terima kasih atas informasi tentang properti acak dalam pesan log.
Mel
@Mel, bisakah Anda mengonfirmasi bahwa baris terakhir Dashboardfile Anda adalah export default withAuthentication(Dashboard);(dan tidak withFirebase)
samthecodingman
Dikonfirmasi withFirebase tergabung dalam withAuthentication - sehingga dapat diambil melalui HOC tersebut.
Mel
@Mel, bisakah Anda memeriksa pesan yang saya kirim di Slack
samthecodingman
0

Jika ada orang lain yang macet, saya menemukan solusinya di sini: Firebase & React: CollectionReference.doc () tipe argumen

Itu tidak berfungsi pada halaman refresh (masih melempar kesalahan bahwa uid adalah nol) tetapi bereaksi kait untuk menggunakan Efek harus mengganti fungsi componentDidMount dengan kombinasi Mount dan Perbarui. Saya coba yang berikutnya.

Mel
sumber