React Hooks ile Firebase dinleyicisi


27

Bulut firestore verileri tepki kanca güncellemeleri ile yenilenir, böylece bir Firebase dinleyici kullanmayı anlamaya çalışıyorum.

Başlangıçta, ben firestore veri almak için bir componentDidMount işlevi ile bir sınıf bileşeni kullanarak yaptı.

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

Sayfa güncellendiğinde bu kesiliyor, bu yüzden kancaları tepki vermek için dinleyiciyi nasıl hareket ettireceğimizi anlamaya çalışıyorum.

Ben yüklemiş tepki-Firebase-kanca aracı - İşe onu elde edebilmek için talimatları okumak anlamaya olamaz rağmen.

Aşağıdaki gibi bir işlev bileşeni var:

import React, { useState, useEffect } from 'react';
import { useDocument } from 'react-firebase-hooks/firestore';

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, withAuthentication } from '../Session/Index';

function Dashboard2(authUser) {
    const FirestoreDocument = () => {

        const [value, loading, error] = useDocument(
          Firebase.db.doc(authUser.uid),
          //firebase.db.doc(authUser.uid),
          //firebase.firestore.doc(authUser.uid),
          {
            snapshotListenOptions: { includeMetadataChanges: true },
          }
        );
    return (

        <div>    



                <p>
                    {error && <strong>Error: {JSON.stringify(error)}</strong>}
                    {loading && <span>Document: Loading...</span>}
                    {value && <span>Document: {JSON.stringify(value.data())}</span>}
                </p>




        </div>

    );
  }
}

export default withAuthentication(Dashboard2);

Bu bileşen, aşağıdaki gibi rota düzeyinde bir authUser sarmalayıcısına sarılır:

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

Aşağıdaki gibi firestore içine takılan bir firebase.js dosyası var:

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


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


  }

Ayrıca authUser öğesinin ne zaman değiştiğini bilmek için bir dinleyici tanımlar:

onAuthUserListener(next, fallback) {
    // onUserDataListener(next, fallback) {
      return this.auth.onAuthStateChanged(authUser => {
        if (authUser) {
          this.user(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('An error occured -> ', err.code ? err.code + ': ' + err.message : (err.message || err));
            setTimeout(fallback, 0); // escapes this Promise's error handler
          });

        };
        if (!authUser) {
          // user not logged in, call fallback handler
          fallback();
          return;
        }
    });
  };

Sonra benim firebase bağlam kurulumunda var:

import FirebaseContext, { withFirebase } from './Context';
import Firebase from '../../firebase';
export default Firebase;
export { FirebaseContext, withFirebase };

Bağlam aşağıdaki gibi bir withFirebase paketinde kurulur:

import React from 'react';
const FirebaseContext = React.createContext(null);

export const withFirebase = Component => props => (
  <FirebaseContext.Consumer>
    {firebase => <Component {...props} firebase={firebase} />}
  </FirebaseContext.Consumer>
);
export default FirebaseContext;

Daha sonra, withAuthentication HOC'de şu şekilde bir bağlam sağlayıcısı var:

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;

Şu anda - bunu denediğimde, Dashboard2 bileşeninde şöyle bir hata alıyorum:

Firebase 'tanımlanmadı

Küçük firebase denedim ve aynı hatayı aldım.

Ayrıca firebase.firestore ve Firebase.firestore'u denedim. Aynı hatayı alıyorum.

HOC'umu bir işlev bileşeniyle kullanıp kullanamayacağımı merak ediyorum.

Bu demo uygulamasını ve bu blog gönderisini gördüm .

Blogdaki önerileri takiben, aşağıdakilerle yeni bir firebase / contextReader.jsx yaptım:

 import React, { useEffect, useContext } from 'react';
import Firebase from '../../firebase';



export const userContext = React.createContext({
    user: null,
  })

export const useSession = () => {
    const { user } = useContext(userContext)
    return user
  }

  export const useAuth = () => {
    const [state, setState] = React.useState(() => 
        { const user = firebase.auth().currentUser 
            return { initializing: !user, user, } 
        }
    );
    function onChange(user) {
      setState({ initializing: false, user })
    }

    React.useEffect(() => {
      // listen for auth state changes
      const unsubscribe = firebase.auth().onAuthStateChanged(onChange)
      // unsubscribe to the listener when unmounting
      return () => unsubscribe()
    }, [])

    return state
  }  

Sonra App.jsx'imi şu okuyucuya sarmaya çalışıyorum:

function App() {
  const { initializing, user } = useAuth()
  if (initializing) {
    return <div>Loading</div>
  }

    // )
// }
// const App = () => (
  return (
    <userContext.Provider value={{ user }}> 


    <Router>
        <Navigation />
        <Route path={ROUTES.LANDING} exact component={StandardLanding} />

Bunu denediğimde, şöyle bir hata alıyorum:

TypeError: _firebase__WEBPACK_IMPORTED_MODULE_2 __. Default.auth bir işlev değil

Bu hatayla ilgili bu gönderiyi gördüm ve ipliği kaldırmayı ve yeniden yüklemeyi denedim. Fark yaratmıyor.

Demo uygulamasına baktığımda, bağlamın bir 'arayüz' yöntemi kullanılarak oluşturulması gerektiğini gösteriyor. Bunun nereden geldiğini göremiyorum - dokümantasyonda açıklamak için bir referans bulamıyorum.

Bunu takmak için ne yaptığımı denemek dışında talimatlar anlamıyorum.

Gördüğüm bu yazı girişimleri tepki-Firebase kancalarımız kullanmadan Firestore dinlemek için. Cevaplar bu aracın nasıl kullanılacağını anlamaya çalıştığına işaret ediyor.

HOC'lardan kancalara nasıl geçileceğine dair bu mükemmel açıklamayı okudum . Firebase dinleyicisini nasıl entegre edeceğime şaşırdım.

Bunu nasıl yapacağınızı düşünmek için yararlı bir örnek sağlayan bu gönderiyi gördüm . Bunu authListener componentDidMount - veya kullanmaya çalışan Dashboard bileşeninde yapmaya çalışıp çalışmadığımdan emin değilim.

SONRAKİ GİRİŞ Aynı sorunu çözmeye çalışan bu gönderiyi buldum .

Shubham Khatri tarafından sunulan çözümü uygulamaya çalıştığımda, firebase yapılandırmasını aşağıdaki gibi ayarladım:

Aşağıdakilere sahip bir bağlam sağlayıcısı: import React, 'tepki' den {useContext}; Firebase'i '../../firebase' den içe aktar;

const FirebaseContext = React.createContext(); 

export const FirebaseProvider = (props) => ( 
   <FirebaseContext.Provider value={new Firebase()}> 
      {props.children} 
   </FirebaseContext.Provider> 
); 

Bağlam kancası daha sonra:

import React, { useEffect, useContext, useState } from 'react';

const useFirebaseAuthentication = (firebase) => {
    const [authUser, setAuthUser] = useState(null);

    useEffect(() =>{
       const unlisten = 
firebase.auth.onAuthStateChanged(
          authUser => {
            authUser
              ? setAuthUser(authUser)
              : setAuthUser(null);
          },
       );
       return () => {
           unlisten();
       }
    });

    return authUser
}

export default useFirebaseAuthentication;

Sonra index.js Ben App sağlayıcı sağlayıcı olarak sarın:

import React from 'react';
import ReactDOM from 'react-dom';
import App from './components/App/Index';
import {FirebaseProvider} from './components/Firebase/ContextHookProvider';

import * as serviceWorker from './serviceWorker';


ReactDOM.render(

    <FirebaseProvider> 
    <App /> 
    </FirebaseProvider>,
    document.getElementById('root')
);

    serviceWorker.unregister();

Sonra, bileşende dinleyici kullanmaya çalıştığımda:

import React, {useContext} from 'react';
import { FirebaseContext } from '../Firebase/ContextHookProvider';
import useFirebaseAuthentication from '../Firebase/ContextHook';


const Dashboard2 = (props) => {
    const firebase = useContext(FirebaseContext);
    const authUser = 
useFirebaseAuthentication(firebase);

    return (
        <div>authUser.email</div>
    )
 }

 export default Dashboard2;

Ve hiçbir bileşen veya yetkilendirme sarmalayıcı ile bir yol olarak kullanmaya çalışın:

<Route path={ROUTES.DASHBOARD2} component={Dashboard2} />

Bunu denediğimde, şöyle bir hata alıyorum:

İçe aktarma girişimi denendi: 'FirebaseContext', '../Firebase/ContextHookProvider' öğesinden dışa aktarılmaz.

ContextHookProvider FirebaseContext'i dışa aktarmadığından - FirebaseProvider'ı dışa aktardığından - ancak bunu Dashboard2'de içe aktarmaya çalışmazsam - kullanmaya çalıştığım işlevde erişemediğim için bu hata iletisi mantıklıdır.

Bu girişimin bir yan etkisi, kayıt yöntemimin artık çalışmamasıdır. Şimdi şöyle bir hata mesajı veriyor:

TypeError: null 'doCreateUserWithEmailAndPassword' özelliği okunamıyor

Bu sorunu daha sonra çözeceğim - ancak temel bir yetkilendirme ayarı elde etmek için çalışmayan milyonlarca yol boyunca bu döngünün aylarını içermeyen firebase ile reaksiyonun nasıl kullanılacağını anlamanın bir yolu olmalı. Tepki kancaları ile çalışan firebase (firestore) için bir başlangıç ​​kiti var mı?

Bir sonraki girişim , bu udemy kursundaki yaklaşımı takip etmeye çalıştım - ancak sadece bir form girişi oluşturmak için çalışıyor - kimliği doğrulanmış kullanıcıyla uyum sağlamak için rotaların etrafına yerleştirilecek bir dinleyici yok.

Çalışmak için bu repo sahip bu youtube öğretici yaklaşım takip etmeye çalıştım . Kancaların nasıl kullanılacağını gösterir, ancak bağlamın nasıl kullanılacağını göstermez.

SONRAKİ ÇÖZÜM Firestore ile kanca kullanma konusunda iyi düşünülmüş bir yaklaşıma sahip gibi görünen bu repoyu buldum . Ancak, kodu anlamıyorum.

Bunu klonladım - ve tüm genel dosyaları eklemeye çalıştım ve sonra çalıştırdığımda - aslında kodu çalıştırmak için alamıyorum. Kodda bu sorunu çözmeye yardımcı olabilecek dersler olup olmadığını görmek için bu çalıştırmak için nasıl yönergeler eksik emin değilim.

SONRAKİ GİRİŞ

Firebase için kurulum olarak ilan edilen divjoy şablonunu satın aldım (başka bir seçenek olarak düşünüyorsa firestore için ayarlanmamıştır).

Bu şablon, uygulamanın yapılandırmasını başlatan bir kimlik doğrulama paketi önerir - ancak yalnızca kimlik doğrulama yöntemleri için - bu nedenle, Firestore için başka bir içerik sağlayıcısına izin vermek üzere yeniden yapılandırılması gerekir. Bu işlemle geçinip ve gösterilen işlemini kullandığınızda bu yazı , ne kaldı şu callback'inde bir hatadır:

useEffect(() => {
    const unsubscribe = firebase.auth().onAuthStateChanged(user => {
      if (user) {
        setUser(user);
      } else {
        setUser(false);
      }
    });

Firebase'in ne olduğunu bilmiyor. Çünkü içe aktarılan ve (useProvideAuth () işlevinde) tanımlanmış olan firebase bağlam sağlayıcısında şu şekilde tanımlanır:

  const firebase = useContext(FirebaseContext)

Geri arama şansı olmadan hata şöyle diyor:

React Hook useEffect'in bağımlılığı eksik: 'firebase'. Ya ekleyin ya da bağımlılık dizisini kaldırın

Veya, ben bu const geri aramaya eklemek denerseniz, bir hata mesajı alıyorum:

React Hook "useContext" bir geri arama içinde çağrılamaz. Reaksiyon Kancaları bir Reaksiyon işlevi bileşeninde veya özel bir Reaksiyon Kancası işlevinde çağrılmalıdır

SONRAKİ GİRİŞ

Firebase yapılandırma dosyamı yalnızca yapılandırma değişkenlerine indirdim (kullanmak istediğim her içerik için içerik sağlayıcılarına yardımcılar yazacağım).

import firebase from 'firebase/app';
import 'firebase/auth';
import 'firebase/firestore';

const devConfig = {
    apiKey: process.env.REACT_APP_DEV_API_KEY,
    authDomain: process.env.REACT_APP_DEV_AUTH_DOMAIN,
    databaseURL: process.env.REACT_APP_DEV_DATABASE_URL,
    projectId: process.env.REACT_APP_DEV_PROJECT_ID,
    storageBucket: process.env.REACT_APP_DEV_STORAGE_BUCKET,
    messagingSenderId: process.env.REACT_APP_DEV_MESSAGING_SENDER_ID,
    appId: process.env.REACT_APP_DEV_APP_ID

  };


  const prodConfig = {
    apiKey: process.env.REACT_APP_PROD_API_KEY,
    authDomain: process.env.REACT_APP_PROD_AUTH_DOMAIN,
    databaseURL: process.env.REACT_APP_PROD_DATABASE_URL,
    projectId: process.env.REACT_APP_PROD_PROJECT_ID,
    storageBucket: process.env.REACT_APP_PROD_STORAGE_BUCKET,
    messagingSenderId: 
process.env.REACT_APP_PROD_MESSAGING_SENDER_ID,
    appId: process.env.REACT_APP_PROD_APP_ID
  };

  const config =
    process.env.NODE_ENV === 'production' ? prodConfig : devConfig;


class Firebase {
  constructor() {
    firebase.initializeApp(config);
    this.firebase = firebase;
    this.firestore = firebase.firestore();
    this.auth = firebase.auth();
  }
};

export default Firebase;  

Sonra aşağıdaki gibi bir Auth içerik sağlayıcısı var:

import React, { useState, useEffect, useContext, createContext } from "react";
import Firebase from "../firebase";

const authContext = createContext();

// Provider component that wraps app and makes auth object ...
// ... available to any child component that calls useAuth().
export function ProvideAuth({ children }) {
  const auth = useProvideAuth();

  return <authContext.Provider value={auth}>{children}</authContext.Provider>;
}

// Hook for child components to get the auth object ...
// ... and update when it changes.
export const useAuth = () => {

  return useContext(authContext);
};

// Provider hook that creates auth object and handles state
function useProvideAuth() {
  const [user, setUser] = useState(null);


  const signup = (email, password) => {
    return Firebase
      .auth()
      .createUserWithEmailAndPassword(email, password)
      .then(response => {
        setUser(response.user);
        return response.user;
      });
  };

  const signin = (email, password) => {
    return Firebase
      .auth()
      .signInWithEmailAndPassword(email, password)
      .then(response => {
        setUser(response.user);
        return response.user;
      });
  };



  const signout = () => {
    return Firebase
      .auth()
      .signOut()
      .then(() => {
        setUser(false);
      });
  };

  const sendPasswordResetEmail = email => {
    return Firebase
      .auth()
      .sendPasswordResetEmail(email)
      .then(() => {
        return true;
      });
  };

  const confirmPasswordReset = (password, code) => {
    // Get code from query string object
    const resetCode = code || getFromQueryString("oobCode");

    return Firebase
      .auth()
      .confirmPasswordReset(resetCode, password)
      .then(() => {
        return true;
      });
  };

  // Subscribe to user on mount
  useEffect(() => {

    const unsubscribe = firebase.auth().onAuthStateChanged(user => {
      if (user) {
        setUser(user);
      } else {
        setUser(false);
      }
    });

    // Subscription unsubscribe function
    return () => unsubscribe();
  }, []);

  return {
    user,
    signup,
    signin,
    signout,
    sendPasswordResetEmail,
    confirmPasswordReset
  };
}

const getFromQueryString = key => {
  return queryString.parse(window.location.search)[key];
};

Ayrıca aşağıdaki gibi bir firebase içerik sağlayıcısı yaptım:

import React, { createContext } from 'react';
import Firebase from "../../firebase";

const FirebaseContext = createContext(null)
export { FirebaseContext }


export default ({ children }) => {

    return (
      <FirebaseContext.Provider value={ Firebase }>
        { children }
      </FirebaseContext.Provider>
    )
  }

Sonra, index.js'de uygulamayı firebase sağlayıcısına sararım

ReactDom.render(
    <FirebaseProvider>
        <App />
    </FirebaseProvider>, 
document.getElementById("root"));

serviceWorker.unregister();

ve güzergah listemde, kimlik doğrulaması sağlayıcısına ilgili güzergahları tamamladım:

import React from "react";
import IndexPage from "./index";
import { Switch, Route, Router } from "./../util/router.js";

import { ProvideAuth } from "./../util/auth.js";

function App(props) {
  return (
    <ProvideAuth>
      <Router>
        <Switch>
          <Route exact path="/" component={IndexPage} />

          <Route
            component={({ location }) => {
              return (
                <div
                  style={{
                    padding: "50px",
                    width: "100%",
                    textAlign: "center"
                  }}
                >
                  The page <code>{location.pathname}</code> could not be found.
                </div>
              );
            }}
          />
        </Switch>
      </Router>
    </ProvideAuth>
  );
}

export default App;

Bu özel denemede, bu hatayla daha önce işaretlenen soruna geri döndüm:

TypeError: _firebase__WEBPACK_IMPORTED_MODULE_2 __. Default.auth bir işlev değil

Sorunu oluştururken kimlik doğrulama sağlayıcısının bu satırına işaret eder:

useEffect(() => {

    const unsubscribe = firebase.auth().onAuthStateChanged(user => {
      if (user) {
        setUser(user);
      } else {
        setUser(false);
      }
    });

Firebase büyük harf F kullanmayı denedim ve aynı hatayı üretir.

Tristan'ın tavsiyelerini denediğimde, tüm bunları kaldırıyorum ve abonelikten çıkma yöntemimi liste dışı bir yöntem olarak tanımlamaya çalışıyorum (neden ateşbaz dilini kullanmadığını bilmiyorum - ama yaklaşımı işe yarasaysa daha çok denerdim nedenini bulmak için). Çözümünü kullanmaya çalıştığımda hata mesajı şöyle diyor:

TypeError: _util_contexts_Firebase__WEBPACK_IMPORTED_MODULE_8 ___ varsayılan (...) bir işlev değil

Cevabı bu yazı auth sonra gelen () çıkarmadan önerir. Bunu denediğimde, bir hata mesajı alıyorum:

TypeError: undefined öğesinin 'onAuthStateChanged' özelliği okunamıyor

Ancak bu yazı , ateş tabanının kimlik doğrulama dosyasına aktarılma biçimiyle ilgili bir sorun olduğunu gösterir.

Ben ithal var: "../firebase" ithal Firebase;

Firebase sınıfın adıdır.

Tristan'ın önerdiği videolar yararlı bir arka plan, ancak şu anda bölüm 9'dayım ve hala bu sorunu çözmeye yardımcı olması gereken kısmı bulamadım. Bunu nerede bulacağını bilen var mı?

SONRAKİ GİRİŞ Sonraki - ve sadece bağlam sorununu çözmeye çalışıyorum - Ben hem createContext hem de useContext ithal ve bu belgelerde gösterildiği gibi bunları kullanmaya çalıştım.

Bir hata iletisini geçemiyorum:

Hata: Geçersiz kanca çağrısı. Kancalar yalnızca bir işlev bileşeninin gövdesi içinde çağrılabilir. Bu, aşağıdaki nedenlerden biriyle olabilir: ...

Bu sorunu çözmek ve çözmek için bu bağlantıdaki önerilerden geçtim ve anlayamıyorum . Bu sorun giderme kılavuzunda gösterilen herhangi bir sorunum yok.

Şu anda - bağlam ifadesi aşağıdaki gibi görünüyor:

import React, {  useContext } from 'react';
import Firebase from "../../firebase";


  export const FirebaseContext = React.createContext();

  export const useFirebase = useContext(FirebaseContext);

  export const FirebaseProvider = props => (
    <FirebaseContext.Provider value={new Firebase()}>
      {props.children}
    </FirebaseContext.Provider>
  );  

Bu udemy dersini kullanarak bu problemin bağlamını ve kancalarını anlamaya çalışmak için zaman harcadım - onu izledikten sonra - aşağıda Tristan tarafından önerilen çözümün tek yönü, createContext yönteminin gönderisinde doğru şekilde çağrılmamasıdır. "React.createContext" olması gerekiyor, ancak yine de sorunu çözmeye yakın bir yere ulaşamıyor.

Hala sıkıştım.

Burada neyin ters gittiğini kimse görebiliyor mu?


İçe aktarmadığınız için tanımsız.
Josh Pittman

3
Sadece eklemeniz gerekir exportiçin export const FirebaseContext?
Federkun

Merhaba Mel yavaş cevap için özür dilerim, işte çılgın bir iki hafta geçirdim, bu yüzden akşam bir bilgisayara bakmak söz konusu değildi! Kontrol edebileceğiniz temiz ve çok basit bir çözüm sağlamak için cevabımı güncelledim.
Tristan Trainer

Çok teşekkürler - şimdi bakacağım.
Mel

Hey Mel, sadece gerçek zamanlı güncellemelerin doğru bir şekilde mağazadan güncellenmesi ile daha da güncellendi (gerçek zamanlı olmamak için onSnapshot parçasını kaldırabilir) Bu çözümse, lütfen sorunuzu bir çok daha kısa ve daha özlü böylece görüntüleyen diğerleri de çözüm bulabilir, teşekkürler - yanıtların yavaş doğası için tekrar özür dilerim
Tristan Trainer

Yanıtlar:


12

Büyük Düzenleme : Bu biraz daha bakmak için biraz zaman aldı bu daha temiz bir çözüm geldi, birisi bana bu yaklaşmak için iyi bir yol olduğu konusunda benimle aynı fikirde olmayabilir.

Farebase Kimlik Doğrulama Kancası

import { useEffect, useState, useCallback } from 'react';
import firebase from 'firebase/app';
import 'firebase/auth';

const firebaseConfig = {
  apiKey: "xxxxxxxxxxxxxx",
  authDomain: "xxxx.firebaseapp.com",
  databaseURL: "https://xxxx.firebaseio.com",
  projectId: "xxxx",
  storageBucket: "xxxx.appspot.com",
  messagingSenderId: "xxxxxxxx",
  appId: "1:xxxxxxxxxx:web:xxxxxxxxx"
};

firebase.initializeApp(firebaseConfig)

const useFirebase = () => {
  const [authUser, setAuthUser] = useState(firebase.auth().currentUser);

  useEffect(() => {
    const unsubscribe = firebase.auth()
      .onAuthStateChanged((user) => setAuthUser(user))
    return () => {
      unsubscribe()
    };
  }, []);

  const login = useCallback((email, password) => firebase.auth()
    .signInWithEmailAndPassword(email, password), []);

  const logout = useCallback(() => firebase.auth().signOut(), [])

  return { login, authUser, logout }
}

export { useFirebase }

AuthUser öğesi null olursa, kimliği doğrulanmamışsa, kullanıcının bir değeri varsa, kimliği doğrulanır.

firebaseConfigFirebase Konsolunda bulunabilir => Proje Ayarları => Uygulamalar => Radyo Düğmesini Yapılandır

useEffect(() => {
  const unsubscribe = firebase.auth()
    .onAuthStateChanged(setAuthUser)

  return () => {
    unsubscribe()
  };
}, []);

Bu useEffect kancası, bir kullanıcının authChanges'ini izlemek için temeldir. AuthUser değerini güncelleyen firebase.auth () öğesinin onAuthStateChanged olayına bir dinleyici ekliyoruz. Bu yöntem, useFirebase kancası yenilendiğinde dinleyiciyi temizlemek için kullanabileceğimiz bu dinleyicinin aboneliğini iptal etmek için bir geri arama döndürür.

Firebase kimlik doğrulaması için ihtiyacımız olan tek kanca budur (firestore vb. İçin diğer kancalar yapılabilir.

const App = () => {
  const { login, authUser, logout } = useFirebase();

  if (authUser) {
    return <div>
      <label>User is Authenticated</label>
      <button onClick={logout}>Logout</button>
    </div>
  }

  const handleLogin = () => {
    login("name@email.com", "password0");
  }

  return <div>
    <label>User is not Authenticated</label>
    <button onClick={handleLogin}>Log In</button>
  </div>
}

Bu, bir Appbileşeninin temel bir uygulamasıdır.create-react-app

useFirestore Veritabanı Kancası

const useFirestore = () => {
  const getDocument = (documentPath, onUpdate) => {
    firebase.firestore()
      .doc(documentPath)
      .onSnapshot(onUpdate);
  }

  const saveDocument = (documentPath, document) => {
    firebase.firestore()
      .doc(documentPath)
      .set(document);
  }

  const getCollection = (collectionPath, onUpdate) => {
    firebase.firestore()
      .collection(collectionPath)
      .onSnapshot(onUpdate);
  }

  const saveCollection = (collectionPath, collection) => {
    firebase.firestore()
      .collection(collectionPath)
      .set(collection)
  }

  return { getDocument, saveDocument, getCollection, saveCollection }
}

Bu, bileşeninize şu şekilde uygulanabilir:

const firestore = useFirestore();
const [document, setDocument] = useState();

const handleGet = () => {
  firestore.getDocument(
    "Test/ItsWFgksrBvsDbx1ttTf", 
    (result) => setDocument(result.data())
  );
}

const handleSave = () => {
  firestore.saveDocument(
    "Test/ItsWFgksrBvsDbx1ttTf", 
    { ...document, newField: "Hi there" }
  );

}

Bu, güncellemeleri doğrudan ateş tabanının kendisinden aldığımız için React useContext'e olan ihtiyacı ortadan kaldırır.

Birkaç şeye dikkat edin:

  1. Değişmeyen bir belgenin kaydedilmesi yeni bir anlık görüntüyü tetiklemez, bu nedenle "fazla kaydetme" yeniden gönderenlere neden olmaz
  2. GetDocument çağrıldığında, onUpdate geri çağrısı hemen bir "anlık görüntü" ile çağrılır, böylece belgenin başlangıç ​​durumunu almak için ekstra koda ihtiyacınız yoktur.

Edit, eski cevabın büyük bir bölümünü kaldırdı


1
Bunun için teşekkürler. Bunu nasıl kurduğunu göremiyorum. Sağlayıcı ile, createContext tanımlı değil diyor bir hata alıyorum. Çünkü şu ana kadar tüketici yok. Sizinkini nereye koydunuz?
Mel

Merhaba üzgünüm createContext tepkinin bir parçası, bu yüzden onu 'reat' den {createContext} olarak içe aktarın Firebase sağlayıcısının nereye gittiğini göstermeyi unuttuğumu fark ettim, cevabı düzenleyeceğim
Tristan Trainer

Bunu sağlayıcıya aktardım, ancak tanımsız hale getiriyor. Bence bunun bir tüketici olmadığı için
Mel

1
Tüketici useContext () kancasıdır, ancak sorunuza tekrar bakarak, FirebaseContext'i dosyadan dışa aktarmamanız gibi görünüyor - bu yüzden bağlamı bulamıyor :)
Tristan Trainer

1
Merhaba @Mel teşekkür ederim, çok nazik, umarım sonunda yardımcı olur. Kancalar ve Firebase her ikisi de oldukça dahil ve kafamı etrafında almak için uzun zaman aldı ve şimdi bile en iyi çözümü bulamamış olabilir, siz kod olarak açıklamak daha kolay olduğu için bir süre benim yaklaşım hakkında bir öğretici oluşturabilir o.
Tristan Trainer

4

Firebase, siz içe aktarmadığınız için tanımlanmamış. İlk olarak, https://github.com/CSFrequency/react-firebase-hooks/tree/master/firestore adresinefirebase.firestore() bağladığınız dokümanlarda örnekte gösterildiği gibi olması gerekir . O zaman ateşbazını gerçekte dosyaya aktarmanız gerekir, böylece ateşbebesi paketi benioku https://www.npmjs.com/package/firebase adresinde belirtildiği gibiimport * as firebase from 'firebase';


1
İndex.js'de içe aktarıyorum
Mel

1
ReactDOM.render (<FirebaseContext.Provider değeri = {new Firebase ()}> <App /> </FirebaseContext.Provider>, document.getElementById ('root'));
Mel

1
Bu yüzden yaklaşım componentDidMount ile çalışır
Mel

1
WithAuth HOC aynı zamanda Firebase ile de sarılır.
Mel

3
Fakat hatanız bunun tanımsız olduğunu söylüyor. Bunu tanımlamanıza yardımcı oluyorum ve çözümün işe yarayıp yaramadığını ya da ortaya çıkan hatanın ne olduğunu söylemiyorsunuz. Mel'e yardım etmeyi her zaman çok zorlaştırıyorsun. Demek istediğim, onu referansta bulunduğunuz dashboard2 bileşen dosyası dışında bir dosyaya alıyorsunuz, bu da hataya neden oluyor. Dizinde bir şeyi içe aktarmak, yapınızın tamamen farklı bir dosyada ne olduğunu anlamasına yardımcı olmaz.
Josh Pittman

2

DÜZENLEME (3 Mart 2020):

Sıfırdan başlayalım.

  1. Yeni bir proje oluşturdum:

    iplik tepki uygulama ateşlemek-kanca-sorunu oluşturmak

  2. Varsayılan olarak oluşturulan 3 App * dosyasını sildim, index.js'den içe aktarmayı kaldırdım ve ayrıca böyle bir temiz index.js'ye sahip olmak için servis çalışanını kaldırdım:

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';

const App = () => {
    return (
        <div>
            Hello Firebase!            
        </div>
    );
};

ReactDOM.render(<App />, document.getElementById('root'));
  1. Merhaba Firebase görmek için uygulamayı başlattım ! yazdırılır.
  2. Firebase modülü ekledim
yarn add firebase
  1. Bu proje için firebase kurmak için firebase init çalıştırdım. Boş firebase projelerimden birini seçtim ve sonraki dosyaları oluşturmakla sonuçlanan Veritabanı ve Firestore'u seçtim:
.firebaserc
database.rules.json
firebase.json
firestore.indexes.json
firestore.rules
  1. Ben Firebase kütüphanelerini için içe aktarma eklendi ve aynı zamanda bir oluşturduk Firebase sınıf ve FirebaseContext . Sonunda Uygulamayı FirebaseContext.Provider bileşenine tamamladım ve değerini yeni bir Firebase () örneğine ayarladım . Bu, yalnızca bir Firebase uygulamasının olması gerektiği gibi somutlaştırılacağından, tek birton olması gerektiğiydi:
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";

import app from "firebase/app";
import "firebase/database";
import "firebase/auth";
import "firebase/firestore";

class Firebase {
    constructor() {
        app.initializeApp(firebaseConfig);

        this.realtimedb = app.database();
        this.firestore = app.firestore();
    }
}

const FirebaseContext = React.createContext(null);

const firebaseConfig = {
    apiKey: "",
    authDomain: "",
    databaseURL: "",
    projectId: "",
    storageBucket: "",
    messagingSenderId: "",
    appId: "",
};

const App = () => {
    return <div>Hello Firebase!</div>;
};

ReactDOM.render(
    <FirebaseContext.Provider value={new Firebase()}>
        <App />
    </FirebaseContext.Provider>
    , document.getElementById("root"));
  1. Firestore'dan her şeyi okuyabildiğimizi doğrulayalım. İlk sadece okuma doğrulamak için ben Firebase Konsolu projemde adlandırılan yeni bir koleksiyon benim Bulut Firestore veritabanını açın ve katma gitti sayaçları bir belge ile basit denilen bir alan içeren değer tipi sayısı ve değeri 0. resim açıklamasını buraya girin resim açıklamasını buraya girin

  2. Sonra oluşturduğumuz FirebaseContext'i kullanmak için App sınıfını güncelledim, basit sayaç kancamız için useState kancasını kullandım ve firestore'dan değeri okumak için useEffect kancasını kullandım:

import React from "react";
import ReactDOM from "react-dom";
import "./index.css";

import app from "firebase/app";
import "firebase/database";
import "firebase/auth";
import "firebase/firestore";

const firebaseConfig = {
    apiKey: "",
    authDomain: "",
    databaseURL: "",
    projectId: "",
    storageBucket: "",
    messagingSenderId: "",
    appId: "",
};

class Firebase {
    constructor() {
        app.initializeApp(firebaseConfig);

        this.realtimedb = app.database();
        this.firestore = app.firestore();
    }
}

const FirebaseContext = React.createContext(null);

const App = () => {
    const firebase = React.useContext(FirebaseContext);
    const [counter, setCounter] = React.useState(-1);

    React.useEffect(() => {
        firebase.firestore.collection("counters").doc("simple").get().then(doc => {
            if(doc.exists) {
                const data = doc.data();
                setCounter(data.value);
            } else {
                console.log("No such document");
            }
        }).catch(e => console.error(e));
    }, []);

    return <div>Current counter value: {counter}</div>;
};

ReactDOM.render(
    <FirebaseContext.Provider value={new Firebase()}>
        <App />
    </FirebaseContext.Provider>
    , document.getElementById("root"));

Not: Cevabı olabildiğince kısa tutmak için, firestore'a test modunda (firestore.rules dosyası) erişim ayarlayarak firebase ile kimlik doğrulamanızın gerekmediğinden emin oldum:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {

    // This rule allows anyone on the internet to view, edit, and delete
    // all data in your Firestore database. It is useful for getting
    // started, but it is configured to expire after 30 days because it
    // leaves your app open to attackers. At that time, all client
    // requests to your Firestore database will be denied.
    //
    // Make sure to write security rules for your app before that time, or else
    // your app will lose access to your Firestore database
    match /{document=**} {
      allow read, write: if request.time < timestamp.date(2020, 4, 8);
    }
  }
}

Önceki cevabım: Tepki-ateş tabanım-auth-iskeletime bir göz atmaktan daha fazlasını bekliyorsunuz:

https://github.com/PompolutZ/react-firebase-auth-skeleton

Esas olarak makaleyi takip eder:

https://www.robinwieruch.de/complete-firebase-authentication-react-tutorial

Ama kancaları kullanmak için biraz yeniden yazılmış. En az iki projemde kullandım.

Mevcut evcil hayvan projemden tipik kullanım:

import React, { useState, useEffect, useContext } from "react";
import ButtonBase from "@material-ui/core/ButtonBase";
import Typography from "@material-ui/core/Typography";
import DeleteIcon from "@material-ui/icons/Delete";
import { FirebaseContext } from "../../../firebase";
import { useAuthUser } from "../../../components/Session";
import { makeStyles } from "@material-ui/core/styles";

const useStyles = makeStyles(theme => ({
    root: {
        flexGrow: 1,
        position: "relative",
        "&::-webkit-scrollbar-thumb": {
            width: "10px",
            height: "10px",
        },
    },

    itemsContainer: {
        position: "absolute",
        top: 0,
        left: 0,
        right: 0,
        bottom: 0,
        display: "flex",
        alignItems: "center",
        overflow: "auto",
    },
}));

export default function LethalHexesPile({
    roomId,
    tokens,
    onSelectedTokenChange,
}) {
    const classes = useStyles();
    const myself = useAuthUser();
    const firebase = useContext(FirebaseContext);
    const pointyTokenBaseWidth = 95;
    const [selectedToken, setSelectedToken] = useState(null);

    const handleTokenClick = token => () => {
        setSelectedToken(token);
        onSelectedTokenChange(token);
    };

    useEffect(() => {
        console.log("LethalHexesPile.OnUpdated", selectedToken);
    }, [selectedToken]);

    const handleRemoveFromBoard = token => e => {
        console.log("Request remove token", token);
        e.preventDefault();
        firebase.updateBoardProperty(roomId, `board.tokens.${token.id}`, {
            ...token,
            isOnBoard: false,
            left: 0,
            top: 0,
            onBoard: { x: -1, y: -1 },
        });
        firebase.addGenericMessage2(roomId, {
            author: "Katophrane",
            type: "INFO",
            subtype: "PLACEMENT",
            value: `${myself.username} removed lethal hex token from the board.`,
        });
    };

    return (
        <div className={classes.root}>
            <div className={classes.itemsContainer}>
                {tokens.map(token => (
                    <div
                        key={token.id}
                        style={{
                            marginRight: "1rem",
                            paddingTop: "1rem",
                            paddingLeft: "1rem",
                            filter:
                            selectedToken &&
                            selectedToken.id === token.id
                                ? "drop-shadow(0 0 10px magenta)"
                                : "",
                            transition: "all .175s ease-out",
                        }}
                        onClick={handleTokenClick(token)}
                    >
                        <div
                            style={{
                                width: pointyTokenBaseWidth * 0.7,
                                position: "relative",
                            }}
                        >
                            <img
                                src={`/assets/tokens/lethal.png`}
                                style={{ width: "100%" }}
                            />
                            {selectedToken && selectedToken.id === token.id && (
                                <ButtonBase
                                    style={{
                                        position: "absolute",
                                        bottom: "0%",
                                        right: "0%",
                                        backgroundColor: "red",
                                        color: "white",
                                        width: "2rem",
                                        height: "2rem",
                                        borderRadius: "1.5rem",
                                        boxSizing: "border-box",
                                        border: "2px solid white",
                                    }}
                                    onClick={handleRemoveFromBoard(token)}
                                >
                                    <DeleteIcon
                                        style={{
                                            width: "1rem",
                                            height: "1rem",
                                        }}
                                    />
                                </ButtonBase>
                            )}
                        </div>
                        <Typography>{`${token.id}`}</Typography>
                    </div>
                ))}
            </div>
        </div>
    );
}

Buradaki en önemli iki parça şunlardır: - geçerli kimliği doğrulanmış kullanıcıyı sağlayan useAuthUser () kancası. - useContext hook ile kullandığım FirebaseContext .

const firebase = useContext(FirebaseContext);

Firebase bağlamınız olduğunda, firebase nesnesini beğeninize uygulamak size kalmıştır. Bazen bazı yararlı işlevler yazıyorum, bazen sadece mevcut bileşenim için oluşturduğum etkili kancada dinleyicileri kurmak daha kolay .

Bu makalenin en iyi bölümlerinden biri , sayfaya hem bileşenin kendisinde erişmek için önkoşulları belirtmenize olanak tanıyan Yetkilendirme HOC'sinin oluşturulmasıydı :

const condition = authUser => authUser && !!authUser.roles[ROLES.ADMIN];
export default withAuthorization(condition)(AdminPage);

Ya da belki bu koşulları doğrudan yönlendirici uygulamanızda ayarlar.

Repo ve makaleye bakmanın, sorunuzun diğer parlak cevaplarını geliştirmek için size ekstra iyi düşünceler vereceğini umuyoruz.


Kitabını satın aldım ve yaklaşımını takip ettim. Koşullar yaklaşımının gerçekte uygulandığında işe yaramadığını ve bu kitapta belirtilen kimlik doğrulama protokolünün bileşen güncellemeleri yoluyla durumunu koruyamadığını gördüm. O kitapta belirtilenleri kullanmanın bir yolunu bulamadım. Yine de düşüncelerinizi paylaştığınız için teşekkürler.
Mel

Ne demek istediğinizi tam olarak anlamadım. Firebase projenizle iskelet uygulamamı denediniz mi? Tüm koşullar anlayabildiğim kadarıyla çalışıyor, çünkü en az 3 projede kullanıyorum.
fxdxpz
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.