gpt4 book ai didi

javascript - 带有React Hooks的Firebase监听器

转载 作者:行者123 更新时间:2023-12-03 13:01:41 27 4
gpt4 key购买 nike

我试图弄清楚如何使用Firebase侦听器,以便使用react hooks更新来刷新云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>
)} />

我有一个firebase.js文件,该文件可按如下方式插入firestore:
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' is not defined



我尝试使用小写的Firebase并得到相同的错误。

我还尝试了firebase.firestore和Firebase.firestore。我犯了同样的错误。

我想知道我是否不能将HOC与功能组件一起使用?

我看过这个 demo app和这个 blog post

按照博客中的建议,我使用以下命令创建了一个新的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 is not a function



我已经看到 this post处理该错误,并尝试卸载并重新安装yarn。这没什么区别。

当我查看 demo app时,它建议使用“接口(interface)”方法创建上下文。我看不到它的来源-在文档中找不到解释它的引用。

除了尝试将其插入外,我无法理解这些说明。

我见过 this post尝试不使用react-firebase-hooks来监听firestore。答案指向试图弄清楚如何使用此工具。

我已经阅读了 this excellent explanation,其中涉及如何从HOC转移到钩子(Hook)。我对如何集成Firebase侦听器感到困惑。

我看过 this post,它为如何考虑这样做提供了一个有用的示例。不知道我是否应该在authListener componentDidMount-或试图使用它的仪表板组件中尝试这样做。

下一个尝试
我找到了 this post,它正在尝试解决相同的问题。

当我尝试实现Shubham Khatri提供的解决方案时,我按如下所示设置了Firebase配置:

具有以下内容的上下文提供程序:
从'react'导入React,{useContext};
从'../../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} />

尝试此操作时,出现错误消息:

Attempted import error: 'FirebaseContext' is not exported from '../Firebase/ContextHookProvider'.



该错误消息是有道理的,因为ContextHookProvider不会导出FirebaseContext-它会导出FirebaseProvider-但是如果我不尝试在Dashboard2中导入它,那么我将无法在尝试使用它的函数中对其进行访问。

这种尝试的副作用是我的注册方法不再起作用。现在,它会生成一条错误消息,内容为:

TypeError: Cannot read property 'doCreateUserWithEmailAndPassword' of null



稍后我将解决此问题-但必须有一种方法来弄清楚如何通过无数种无法获得基本身份验证设置的途径,在不涉及此循环数月的firebase上使用react。是否有可与React挂钩一起使用的Firebase(firestore)入门套件?

下次尝试
我尝试遵循此 udemy course中的方法-但它仅适用于生成表单输入-没有监听器可以绕过路由以与已验证用户进行调整。

我尝试遵循此 youtube tutorial中的方法-使用该 repo。它显示了如何使用挂钩,但没有显示上下文。

下一个尝试
我发现 this repo在使用带Firestore的钩子(Hook)时似乎有一种经过深思熟虑的方法。但是,我无法理解代码。

我克隆了它-试图添加所有公共(public)文件,然后在我运行它时-我实际上无法使代码运行。我不确定如何运行它的说明中缺少了什么,以便查看代码中是否有可以帮助解决此问题的类(class)。

下一个尝试

我购买了divjoy模板,该模板的广告宣传是为firebase设置的(如果其他人考虑将其作为选项,则不是为firestore设置的)。

该模板提出了一个auth包装器,用于初始化应用程序的配置-但仅用于auth方法-因此,需要对其进行重组以允许其他上下文提供程序用于firestore。当您混淆该过程并使用 this post中所示的过程时,剩下的是以下回调中的错误:
useEffect(() => {
const unsubscribe = firebase.auth().onAuthStateChanged(user => {
if (user) {
setUser(user);
} else {
setUser(false);
}
});

它不知道什么是火力基地。这是因为它是在firebase上下文提供程序中定义的,该提供程序被导入并定义为(在useProvideAuth()函数中)为:
  const firebase = useContext(FirebaseContext)

没有机会进行回调,错误提示:

React Hook useEffect has a missing dependency: 'firebase'. Either include it or remove the dependency array



或者,如果我尝试将该const添加到回调中,则会收到错误消息:

React Hook "useContext" cannot be called inside a callback. React Hooks must be called in a React function component or a custom React Hook function



下一个尝试

我将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();

在我的路由列表中,我已将相关路由包装在auth提供程序中:
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 is not a function



它指向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(...) is not a function



this post的答案建议从auth之后删除()。尝试该操作时,出现错误消息:

TypeError: Cannot read property 'onAuthStateChanged' of undefined



但是 this post建议在身份验证文件中导入Firebase的方式存在问题。

我将其导入为:从“../firebase”导入Firebase;

Firebase是该类的名称。

特里斯坦(Tristan)推荐的视频对背景很有帮助,但我目前在第9集,但仍未找到应该用来解决此问题的部分。有人知道在哪里找到吗?

下一个尝试
接下来-并尝试仅解决上下文问题-我已导入createContext和useContext并尝试使用它们,如本文档中所示。

我无法通过一个错误,说:

Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons: ...



我已经尝试过 this link中的建议来尝试解决此问题,但无法解决。我没有此故障排除指南中显示的任何问题。

当前-上下文语句如下所示:
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 course尝试弄清楚上下文并将该元素挂接到该问题上-观看完之后-下面Tristan提出的解决方案的唯一方面是在他的帖子中未正确调用createContext方法。它必须是“React.createContext”,但仍无法解决问题。

我仍然被卡住。

谁能看到这里发生了什么问题?

最佳答案

主要编辑:
花了一些时间对此进行更多研究,这是我想出的一个更干净的解决方案,可能有人不赞成我这是解决此问题的好方法。

使用Firebase身份验证挂钩

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为null,则不进行身份验证;如果user有值,则进行身份验证。

可以在Firebase上找到 firebaseConfig 控制台 => 项目设置 => 应用程序 => 配置单选按钮
useEffect(() => {
const unsubscribe = firebase.auth()
.onAuthStateChanged(setAuthUser)

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

此useEffect挂钩是跟踪用户authChanges的核心。我们向firebase.auth()的onAuthStateChanged事件添加了一个侦听器,该事件更新了authUser的值。此方法返回用于取消订阅该侦听器的回调,当useFirebase挂钩刷新时,我们可以使用该回调来清理侦听器。

这是我们进行Firebase身份验证所需的唯一挂钩(可以为firestore等制作其他挂钩。
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>
}

这是 Appcreate-react-app组件的基本实现

使用Firestore数据库挂钩
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 }
}

可以像这样在您的组件中实现:
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" }
);

}

然后,当我们直接从firebase本身获取更新时,这消除了对React useContext的需求。

请注意以下几点:
  • 保存未更改的文档不会触发新的快照,因此“过度保存”不会导致重新提交
  • 调用getDocument时,会立即使用初始“​​快照”立即调用onUpdate回调,因此您不需要额外的代码即可获取文档的初始状态。

  • 编辑已删除很大一部分旧答案

    关于javascript - 带有React Hooks的Firebase监听器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59977856/

    27 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com