私は、Firebaseリスナーを使用して、クラウドFirestoreデータが反応フックの更新で更新される方法を理解しようとしています。
最初、私はこれを、componentDidMount関数を持つクラスコンポーネントを使用して作成し、firestoreデータを取得しました。
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,
});
}
それはページが更新されると壊れるので、フックを反応させるためにリスナーを移動する方法を理解しようとしています。
私はreact-firebase-hooksツールをインストールしましたが、それを機能させるための説明を読む方法がわかりません。
次のような関数コンポーネントがあります。
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);
このコンポーネントは、次のようにルートレベルでauthUserラッパーにラップされます。
<Route path={ROUTES.DASHBOARD2} render={props => (
<AuthUserContext.Consumer>
{ authUser => (
<Dashboard2 authUser={authUser} {...props} />
)}
</AuthUserContext.Consumer>
)} />
次のようにfirestoreにプラグインするfirebase.jsファイルがあります。
class Firebase {
constructor() {
app.initializeApp(config).firestore();
/* helpers */
this.fieldValue = app.firestore.FieldValue;
/* Firebase APIs */
this.auth = app.auth();
this.db = app.firestore();
}
また、authUserが変更されたときに認識するリスナーを定義します。
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;
}
});
};
次に、私のfirebaseコンテキスト設定で私は持っています:
import FirebaseContext, { withFirebase } from './Context';
import Firebase from '../../firebase';
export default Firebase;
export { FirebaseContext, withFirebase };
コンテキストはwithFirebaseラッパーで次のように設定されます。
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;
次に、私のwithAuthentication HOCに、次のようなコンテキストプロバイダーがあります。
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;
現在、これを試してみると、Dashboard2コンポーネントで次のエラーが発生します。
Firebase 'は定義されていません
小文字のfirebaseを試しましたが、同じエラーが発生しました。
firebase.firestoreとFirebase.firestoreも試しました。同じエラーが発生します。
HOCを関数コンポーネントで使用できないのでしょうか。
ブログのアドバイスに従って、私は新しいfirebase / contextReader.jsxを次のように作成しました:
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
}
次に、App.jsxをそのリーダーでラップしてみます。
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} />
これを試すと、次のエラーが表示されます。
TypeError:_firebase__WEBPACK_IMPORTED_MODULE_2 __。default.authは関数ではありません
私はこのエラーを扱っているこの投稿を見て、 yarnをアンインストールして再インストールしようとしました。違いはありません。
私が見たときデモアプリケーションは、コンテキストは「インタフェースのメソッドを使用して作成されなければならないことを示唆しています。これがどこから来ているのかわかりません-ドキュメントでそれを説明するためのリファレンスが見つかりません。
私はこれをプラグインするために私がしたことを試す以外に指示を理解することができません。
私はこの投稿を見て、react-firebase-hooksを使用せずにfirestoreを聴こうとしました。答えは、このツールの使用方法を理解しようとすることに戻っています。
HOCからフックに移行する方法に関するこの優れた説明を読みました。firebaseリスナーを統合する方法に行き詰まっています。
私はこれを行うことについて考える方法の有用な例を提供するこの投稿を見ました。これをauthListener componentDidMount-またはそれを使用しようとしているダッシュボードコンポーネントで実行する必要があるかどうかはわかりません。
次の試み 私は同じ問題を解決しようとしているこの投稿を見つけました。
Shubham Khatriが提供するソリューションを実装しようとすると、次のようにfirebase構成をセットアップします。
コンテキストプロバイダー:import React、{useContext} from 'react'; '../../firebase'からFirebaseをインポートします。
const FirebaseContext = React.createContext();
export const FirebaseProvider = (props) => (
<FirebaseContext.Provider value={new Firebase()}>
{props.children}
</FirebaseContext.Provider>
);
コンテキストフックは次のようになります。
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;
次に、index.jsでアプリをプロバイダーにラップします。
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();
次に、私が持っているコンポーネントでリスナーを使用しようとすると:
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;
そして、それをコンポーネントや認証ラッパーのないルートとして使用しようとします:
<Route path={ROUTES.DASHBOARD2} component={Dashboard2} />
これを試すと、次のエラーが表示されます。
インポートしようとしたエラー:「FirebaseContext」は「../Firebase/ContextHookProvider」からエクスポートされません。
ContextHookProviderはFirebaseContextをエクスポートしないため、このエラーメッセージは理にかなっています。FirebaseProviderをエクスポートしますが、これをDashboard2にインポートしようとしないと、それを使用しようとする関数でアクセスできません。
この試みの副作用の1つは、私のサインアップ方法が機能しなくなったことです。次のようなエラーメッセージが生成されます。
TypeError:nullのプロパティ 'doCreateUserWithEmailAndPassword'を読み取れません
この問題は後で解決します。ただし、基本的な認証設定を取得するために機能しない数百万通りのループを何ヶ月もループすることなく、firebaseとの対応方法を理解する方法が必要です。反応フックで動作するfirebase(firestore)のスターターキットはありますか?
次の試みで は、このudemyコースのアプローチを試してみましたが、これはフォーム入力を生成するためにのみ機能します。認証済みユーザーに合わせて調整するためにルートを囲むリスナーはありません。
私はこのyoutubeチュートリアルのアプローチに従いました-これには、このリポジトリが機能します。フックの使い方は示していますが、コンテキストの使い方は示していません。
次の試み 私は、Firestoreでフックを使用するためのよく考えられたアプローチがあるように見えるこのリポジトリを見つけました。ただし、コードを理解することはできません。
私はこれを複製し、すべてのパブリックファイルを追加しようとしましたが、実行すると、実際にコードを動作させることができません。この問題を解決するのに役立つレッスンがコードにあるかどうかを確認するために、これを実行する方法の説明に何が欠けているのかわかりません。
次の試み
私はdivjoyテンプレートを購入しました。これは、firebaseのセットアップとして宣伝されています(他の誰かがこれをオプションとして検討している場合、firestoreのセットアップではありません)。
そのテンプレートは、アプリの構成を初期化する認証ラッパーを提案しますが、認証メソッドのためだけなので、Firestoreの別のコンテキストプロバイダーを許可するように再構成する必要があります。そのプロセスを何とかして、この投稿に示されているプロセスを使用すると、残っているのは次のコールバックのエラーです。
useEffect(() => {
const unsubscribe = firebase.auth().onAuthStateChanged(user => {
if (user) {
setUser(user);
} else {
setUser(false);
}
});
それはfirebaseが何であるかを知りません。これは、(useProvideAuth()関数で)インポートおよび定義されているfirebaseコンテキストプロバイダーで次のように定義されているためです。
const firebase = useContext(FirebaseContext)
コールバックの機会がない場合、エラーは次のように言います。
React Hook useEffectには依存関係がありません: 'firebase'。それを含めるか、依存関係配列を削除してください
または、そのconstをコールバックに追加しようとすると、次のエラーが表示されます。
React Hook "useContext"はコールバック内で呼び出すことはできません。Reactフックは、React関数コンポーネントまたはカスタムのReactフック関数で呼び出す必要があります
次の試み
firebase構成ファイルを構成変数のみに減らしました(使用する各コンテキストのコンテキストプロバイダーにヘルパーを記述します)。
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;
次に、次のように認証コンテキストプロバイダーを用意します。
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];
};
次のようにして、firebaseコンテキストプロバイダーも作成しました。
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>
)
}
次に、index.jsでアプリをfirebaseプロバイダーでラップします
ReactDom.render(
<FirebaseProvider>
<App />
</FirebaseProvider>,
document.getElementById("root"));
serviceWorker.unregister();
私のルートリストでは、関連するルートを認証プロバイダーでラップしています。
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;
この特定の試みでは、以前にこのエラーで報告された問題に戻ります。
TypeError:_firebase__WEBPACK_IMPORTED_MODULE_2 __。default.authは関数ではありません
問題を引き起こしていると、認証プロバイダーのこの行を指します:
useEffect(() => {
const unsubscribe = firebase.auth().onAuthStateChanged(user => {
if (user) {
setUser(user);
} else {
setUser(false);
}
});
Firebaseで大文字のFを使用してみましたが、同じエラーが発生します。
Tristanのアドバイスを試すときは、それらすべてを削除して、unsubscribeメソッドをunlistenメソッドとして定義します(なぜ彼がfirebase言語を使用していないのかわかりませんが、彼のアプローチがうまくいったなら、もっと頑張ろうと思います理由を理解するため)。私が彼のソリューションを使用しようとすると、エラーメッセージは次のようになります。
TypeError:_util_contexts_Firebase__WEBPACK_IMPORTED_MODULE_8 ___ default(...)は関数ではありません
この投稿への答えは、認証後に()を削除することを提案しています。試してみると、次のようなエラーが表示されます。
TypeError:未定義のプロパティ 'onAuthStateChanged'を読み取れません
ただし、この投稿は、firebaseがauthファイルにインポートされる方法に問題があることを示唆しています。
次のようにインポートしました。「../ firebase」からFirebaseをインポートします。
Firebaseはクラスの名前です。
Tristanが推奨する動画は役立つ背景ですが、私は現在エピソード9にいますが、この問題の解決に役立つはずの部分がまだ見つかりません。どこにそれを見つけるか知っていますか?
NEXT ATTEMPT Next-コンテキスト問題のみを解決しようとしている-私はcreateContextとuseContextの両方をインポートし、このドキュメントに示されているようにそれらを使用しようとしました。
私は言うエラーを渡すことができません:
エラー:無効なフック呼び出し。フックは、関数コンポーネントの本体の内部でのみ呼び出すことができます。これは、次のいずれかの理由で発生する可能性があります:...
私はこのリンクの提案を試してこの問題を解決しようとしましたが、理解できません。このトラブルシューティングガイドに示されている問題はありません。
現在、コンテキストステートメントは次のようになっています。
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>
);
私はこのudemyコースを使用して、この問題にコンテキストとフック要素を理解するために時間を費やしました-見てから-以下のTristanによって提案されたソリューションの唯一の側面は、彼の投稿でcreateContextメソッドが正しく呼び出されないことです。それは "React.createContext"である必要がありますが、それでも問題の解決に近づきません。
まだ行き詰まっています。
誰かがここで何が間違っているのか見ることができますか?
export
する必要があるだけexport const FirebaseContext
ですか?