Mel*_*Mel 36 javascript firebase reactjs google-cloud-firestore react-hooks
我想弄清楚如何使用 Firebase 侦听器,以便使用 React 挂钩更新来刷新云 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,
});
}
Run Code Online (Sandbox Code Playgroud)
当页面更新时会中断,所以我试图弄清楚如何移动侦听器以响应钩子。
我已经安装了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);
Run Code Online (Sandbox Code Playgroud)
该组件在路由级别封装在 authUser 包装器中,如下所示:
<Route path={ROUTES.DASHBOARD2} render={props => (
<AuthUserContext.Consumer>
{ authUser => (
<Dashboard2 authUser={authUser} {...props} />
)}
</AuthUserContext.Consumer>
)} />
Run Code Online (Sandbox Code Playgroud)
我有一个 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();
}
Run Code Online (Sandbox Code Playgroud)
它还定义了一个侦听器以了解 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;
}
});
};
Run Code Online (Sandbox Code Playgroud)
然后,在我的 firebase 上下文设置中,我有:
import FirebaseContext, { withFirebase } from './Context';
import Firebase from '../../firebase';
export default Firebase;
export { FirebaseContext, withFirebase };
Run Code Online (Sandbox Code Playgroud)
上下文在 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;
Run Code Online (Sandbox Code Playgroud)
然后,在我的 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;
Run Code Online (Sandbox Code Playgroud)
目前 - 当我尝试这个时,我在 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
}
Run Code Online (Sandbox Code Playgroud)
然后我尝试将我的 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} />
Run Code Online (Sandbox Code Playgroud)
当我尝试这个时,我收到一条错误消息:
类型错误:_firebase__WEBPACK_IMPORTED_MODULE_2__.default.auth 不是函数
我看过这篇处理该错误的帖子,并尝试卸载并重新安装纱线。没有什么不同的。
当我查看演示应用程序时,它建议应该使用“接口”方法创建上下文。我看不出这是从哪里来的 - 我在文档中找不到解释它的参考。
除了尝试我为插入它所做的工作之外,我无法理解这些说明。
我看过这篇文章,它试图在不使用 react-firebase-hooks 的情况下收听 firestore。答案指向试图弄清楚如何使用此工具。
我已经阅读了这个很好的解释,其中介绍了如何从 HOC 转移到钩子。我被困在如何集成 firebase 侦听器上。
我看过这篇文章,它为如何考虑这样做提供了一个有用的例子。不确定我是否应该尝试在 authListener componentDidMount 中执行此操作 - 或在尝试使用它的 Dashboard 组件中执行此操作。
下一次尝试 我找到了这篇文章,它试图解决同样的问题。
当我尝试实施 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>
);
Run Code Online (Sandbox Code Playgroud)
然后上下文挂钩具有:
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;
Run Code Online (Sandbox Code Playgroud)
然后在 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();
Run Code Online (Sandbox Code Playgroud)
然后,当我尝试在组件中使用侦听器时,我有:
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;
Run Code Online (Sandbox Code Playgroud)
我尝试将它用作没有组件或身份验证包装器的路由:
<Route path={ROUTES.DASHBOARD2} component={Dashboard2} />
Run Code Online (Sandbox Code Playgroud)
当我尝试这个时,我收到一条错误消息:
尝试导入错误:“FirebaseContext”未从“../Firebase/ContextHookProvider”导出。
该错误消息是有道理的,因为 ContextHookProvider 不导出 FirebaseContext - 它导出 FirebaseProvider - 但如果我不尝试在 Dashboard2 中导入它 - 那么我无法在尝试使用它的函数中访问它。
这种尝试的一个副作用是我的注册方法不再有效。现在它会生成一条错误消息,内容为:
类型错误:无法读取 null 的属性“doCreateUserWithEmailAndPassword”
我稍后会解决这个问题 - 但必须有一种方法来弄清楚如何使用 react with firebase 不涉及数月的这个循环,通过数百万条无法获得基本身份验证设置的途径。是否有适用于反应钩子的 firebase (firestore) 入门套件?
下一次尝试 我尝试遵循此udemy 课程中的方法- 但它只能生成表单输入 - 没有监听器可以围绕经过身份验证的用户调整路由。
我试图遵循这个youtube 教程中的方法- 有这个repo可以使用。它展示了如何使用钩子,但没有展示如何使用上下文。
下一次尝试 我发现这个 repo似乎有一个深思熟虑的方法来使用带 firestore 的钩子。但是,我无法理解代码。
我克隆了这个 - 并尝试添加所有公共文件,然后当我运行它时 - 我实际上无法让代码运行。我不确定如何让它运行的说明中缺少什么,以便查看代码中是否有可以帮助解决此问题的课程。
下一次尝试
我购买了 divjoy 模板,它被宣传为为 firebase 设置(它不是为 firestore 设置的,以防其他人考虑将此作为一个选项)。
该模板提出了一个 auth 包装器,用于初始化应用程序的配置 - 但仅用于 auth 方法 - 因此需要对其进行重组以允许另一个用于 firestore 的上下文提供程序。当你蒙混过关该过程,并使用所示的处理这个帖子,剩下的就是在下面的回调的错误:
useEffect(() => {
const unsubscribe = firebase.auth().onAuthStateChanged(user => {
if (user) {
setUser(user);
} else {
setUser(false);
}
});
Run Code Online (Sandbox Code Playgroud)
它不知道firebase是什么。那是因为它是在 firebase 上下文提供程序中定义的,该提供程序被导入和定义(在 useProvideAuth() 函数中)为:
const firebase = useContext(FirebaseContext)
Run Code Online (Sandbox Code Playgroud)
没有机会回调,错误说:
React Hook useEffect 缺少依赖项:'firebase'。包括它或删除依赖项数组
或者,如果我尝试将该常量添加到回调中,则会收到一条错误消息:
React Hook "useContext" 不能在回调中调用。React Hooks 必须在 React 函数组件或自定义 React Hook 函数中调用
下一次尝试
我已将我的 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;
Run Code Online (Sandbox Code Playgroud)
然后我有一个身份验证上下文提供程序,如下所示:
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];
};
Run Code Online (Sandbox Code Playgroud)
我还制作了一个 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>
)
}
Run Code Online (Sandbox Code Playgroud)
然后,在 index.js 中,我将应用程序包装在 firebase 提供程序中
ReactDom.render(
<FirebaseProvider>
<App />
</FirebaseProvider>,
document.getElementById("root"));
serviceWorker.unregister();
Run Code Online (Sandbox Code Playgroud)
在我的路线列表中,我已将相关路线包装在身份验证提供程序中:
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;
Run Code Online (Sandbox Code Playgroud)
在这个特殊的尝试中,我又回到了之前用这个错误标记的问题:
类型错误:_firebase__WEBPACK_IMPORTED_MODULE_2__.default.auth 不是函数
它指出身份验证提供程序的这一行是造成问题的原因:
useEffect(() => {
const unsubscribe = firebase.auth().onAuthStateChanged(user => {
if (user) {
setUser(user);
} else {
setUser(false);
}
});
Run Code Online (Sandbox Code Playgroud)
我曾尝试在 Firebase 中使用大写的 F 并产生相同的错误。
当我尝试 Tristan 的建议时,我删除了所有这些东西,并尝试将我的取消订阅方法定义为一个不听的方法(我不知道他为什么不使用 firebase 语言 - 但如果他的方法有效,我会更加努力找出原因)。当我尝试使用他的解决方案时,错误消息说:
类型错误:_util_contexts_Firebase__WEBPACK_IMPORTED_MODULE_8___default(...) 不是函数
这篇文章的答案建议从 after auth 中删除 ()。当我尝试这样做时,我收到一条错误消息:
类型错误:无法读取未定义的属性“onAuthStateChanged”
但是,这篇文章表明在 auth 文件中导入 firebase 的方式存在问题。
我将其导入为:从“../firebase”导入 Firebase;
Firebase 是类的名称。
Tristan 推荐的视频是有用的背景,但我目前正在播放第 9 集,但仍未找到应该有助于解决此问题的部分。有谁知道在哪里可以找到?
下 一个尝试接下来 - 并且仅尝试解决上下文问题 - 我已经导入了 createContext 和 useContext 并尝试使用它们,如本文档中所示。
我无法通过一个错误说:
错误:无效的挂钩调用。钩子只能在函数组件的主体内部调用。这可能是由于以下原因之一造成的:...
我已经通过此链接中的建议尝试解决此问题,但无法弄清楚。我没有遇到本故障排除指南中显示的任何问题。
当前 - 上下文语句如下所示:
import React, { useContext } from 'react';
import Firebase from "../../firebase";
Tri*_*ner 27
主要编辑:花了一些时间对此进行了更多研究,这是我提出的一个更清洁的解决方案,有人可能不同意我的观点,认为这是解决此问题的好方法。
使用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 }
Run Code Online (Sandbox Code Playgroud)
如果 authUser 为 null,则未通过身份验证,如果用户有值,则进行身份验证。
firebaseConfig可以在 firebase控制台=>项目设置=>应用程序=>配置单选按钮上找到
useEffect(() => {
const unsubscribe = firebase.auth()
.onAuthStateChanged(setAuthUser)
return () => {
unsubscribe()
};
}, []);
Run Code Online (Sandbox Code Playgroud)
这个 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>
}
Run Code Online (Sandbox Code Playgroud)
这是一个App组件的基本实现create-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 }
}
Run Code Online (Sandbox Code Playgroud)
这可以在您的组件中实现,如下所示:
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" }
);
Run Code Online (Sandbox Code Playgroud)
}
这消除了对 React useContext 的需求,因为我们直接从 firebase 本身获取更新。
请注意以下几点:
编辑删除了大部分旧答案
编辑(2020 年 3 月 3 日):
让我们从头开始。
我创建了一个新项目:
yarn 创建 react-app firebase-hook-issue
我已经删除了默认创建的所有 3 个 App* 文件,从 index.js 中删除了导入,还删除了 service worker 以获得一个干净的 index.js:
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'));
Run Code Online (Sandbox Code Playgroud)
yarn add firebase
Run Code Online (Sandbox Code Playgroud)
.firebaserc
database.rules.json
firebase.json
firestore.indexes.json
firestore.rules
Run Code Online (Sandbox Code Playgroud)
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"));
Run Code Online (Sandbox Code Playgroud)
让我们验证我们可以从 Firestore 读取任何内容。为了验证刚读第一,我去了我在火力地堡控制台项目中,打开我的云数据库的FireStore并增加了一个名为新的集合计数器与文档简单含有称为一个字段值的类型数量和值0。

然后我更新了 App 类以使用我们创建的 FirebaseContext,为我们的简单计数器钩子制作 useState 钩子,并使用 useEffect 钩子从 firestore 读取值:
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"));
Run Code Online (Sandbox Code Playgroud)
注意:为了使答案尽可能简短,我确保您不需要通过 firebase 进行身份验证,方法是将 firestore 的访问权限设置为测试模式(firestore.rules 文件):
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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
我之前的回答: 非常欢迎您查看我的 react-firebase-auth-skeleton:
https://github.com/PompolutZ/react-firebase-auth-skeleton
它主要遵循文章:
https://www.robinwieruch.de/complete-firebase-authentication-react-tutorial
但有些重写以使用钩子。我至少在我的两个项目中使用过它。
我当前宠物项目的典型用法:
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>
);
}
Run Code Online (Sandbox Code Playgroud)
这里最重要的两个部分是: - useAuthUser()钩子,提供当前经过身份验证的用户。- 我通过useContext钩子使用的 FirebaseContext 。
const firebase = useContext(FirebaseContext);
Run Code Online (Sandbox Code Playgroud)
当您拥有 firebase 的上下文时,您可以根据自己的喜好实现 firebase 对象。有时我确实编写了一些有用的函数,有时在我为当前组件创建的useEffect钩子中设置侦听器更容易。
该文章最好的部分之一是创建withAuthorization HOC,它允许您在组件本身中指定访问页面的先决条件:
const condition = authUser => authUser && !!authUser.roles[ROLES.ADMIN];
export default withAuthorization(condition)(AdminPage);
Run Code Online (Sandbox Code Playgroud)
或者甚至可以在您的路由器实现中正确设置这些条件。
希望查看 repo 和文章会给您一些额外的好想法,以增强对您的问题的其他精彩答案。
Firebase 未定义,因为您没有导入它。首先,它需要如您链接到https://github.com/CSFrequency/react-firebase-hooks/tree/master/firestorefirebase.firestore()的文档中的示例所示。然后,您需要在文件中实际导入 firebase,如firebase 包自述文件https://www.npmjs.com/package/firebase中所述import * as firebase from 'firebase';
| 归档时间: |
|
| 查看次数: |
17282 次 |
| 最近记录: |