Я пытаюсь понять, как использовать прослушиватель Firebase, чтобы данные облачного хранилища обновлялись с помощью обновлений активных перехватчиков.
Первоначально я сделал это, используя компонент класса с функцией componentDidMount, чтобы получить данные о пожарном депо.
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,
});
}
Это ломается, когда страница обновляется, поэтому я пытаюсь понять, как заставить слушателя реагировать на хуки.
Я установил инструмент Reaction-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 'не определено
Я попробовал строчную базу в нижнем регистре и получил ту же ошибку.
Я также попробовал 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} />
Когда я пытаюсь это сделать, я получаю сообщение об ошибке:
Ошибка типа: _firebase__WEBPACK_IMPORTED_MODULE_2 __. Default.auth не является функцией
Я видел этот пост с этой ошибкой и пытался удалить и переустановить пряжу. Это не имеет значения.
Когда я смотрю на демонстрационное приложение , оно предполагает, что контекст должен быть создан с использованием метода 'interface'. Я не вижу, откуда это исходит - я не могу найти ссылку, чтобы объяснить это в документации.
Я не могу понять инструкции, кроме как попробовать то, что я сделал, чтобы подключить это.
Я видел этот пост, в котором делается попытка прослушать firestore без использования активных-firebase-hooks. Ответы указывают на попытку выяснить, как использовать этот инструмент.
Я прочитал это превосходное объяснение, которое объясняет, как перейти от HOCs к крючкам. Я застрял с тем, как интегрировать слушатель Firebase.
Я видел этот пост, который предоставляет полезный пример того, как думать об этом. Не уверен, что я должен пытаться сделать это в компоненте authListenerDidMount - или в компоненте Dashboard, который пытается его использовать.
СЛЕДУЮЩАЯ ПОПЫТКА Я нашел этот пост , который пытается решить ту же проблему.
Когда я пытаюсь реализовать решение, предлагаемое Шубхамом Хатри, я настраивал конфигурацию firebase следующим образом:
Поставщик контекста с: import React, {useContext} из 'response'; импортировать 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 - тогда я не могу получить к нему доступ в функции, которая пытается его использовать.
Одним из побочных эффектов этой попытки является то, что мой метод регистрации больше не работает. Теперь генерируется сообщение об ошибке:
TypeError: Невозможно прочитать свойство 'doCreateUserWithEmailAndPassword' из null
Я решу эту проблему позже - но должен быть способ выяснить, как использовать реакцию с 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 ()) как:
const firebase = useContext(FirebaseContext)
Без шансов на обратный вызов, ошибка говорит:
У React Hook useEffect отсутствует зависимость: 'firebase'. Либо включите его, либо удалите массив зависимостей
Или, если я пытаюсь добавить это const к обратному вызову, я получаю сообщение об ошибке:
React Hook «useContext» не может быть вызван внутри обратного вызова. React Hooks должны вызываться в компоненте функции React или в пользовательской функции React Hooks.
СЛЕДУЮЩАЯ ПОПЫТКА
Я сократил мой файл конфигурации 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;
На этой конкретной попытке я возвращаюсь к проблеме, помеченной ранее с этой ошибкой:
Ошибка типа: _firebase__WEBPACK_IMPORTED_MODULE_2 __. Default.auth не является функцией
Это указывает на эту строку поставщика аутентификации как создающую проблему:
useEffect(() => {
const unsubscribe = firebase.auth().onAuthStateChanged(user => {
if (user) {
setUser(user);
} else {
setUser(false);
}
});
Я попытался использовать заглавную букву F в Firebase, и она выдает ту же ошибку.
Когда я пользуюсь советом Тристана, я удаляю все эти вещи и пытаюсь определить свой метод отмены подписки как метод отсутствия доступа (я не знаю, почему он не использует язык Firebase - но если бы его подход работал, я бы постарался больше выяснить почему). Когда я пытаюсь использовать его решение, появляется сообщение об ошибке:
Ошибка типа: _util_contexts_Firebase__WEBPACK_IMPORTED_MODULE_8 ___ default (...) не является функцией
Ответ на этот пост предлагает удалить () после аутентификации. Когда я пытаюсь это сделать, я получаю сообщение об ошибке:
TypeError: Невозможно прочитать свойство 'onAuthStateChanged' из неопределенного
Однако этот пост предлагает проблему со способом импорта firebase в файле auth.
Я импортировал его как: импорт Firebase из "../firebase";
Firebase это название класса.
Видео, рекомендованные Тристаном, являются полезной информацией, но я в настоящее время нахожусь в эпизоде 9 и все еще не нашел ту часть, которая должна помочь решить эту проблему. Кто-нибудь знает, где это найти?
СЛЕДУЮЩАЯ ПОПЫТКА Далее - и пытаясь решить только проблему контекста - я импортировал как 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, чтобы попытаться выяснить контекст и зацепить элемент этой проблемы - после просмотра - единственный аспект решения, предложенного Тристаном ниже, заключается в том, что метод createContext не вызывается правильно в его посте. это должен быть «React.createContext», но он все еще не подходит для решения проблемы.
Я все еще застрял.
Кто-нибудь может увидеть, что здесь пошло не так?
export
кexport const FirebaseContext
?Ответы:
Major Edit : Потребовалось некоторое время, чтобы разобраться в этом немного подробнее, вот что я придумала, это более чистое решение, кто-то может не согласиться со мной по поводу того, что это хороший способ приблизиться к этому.
UseFirebase Auth Hook
Если authUser имеет значение null, то не аутентифицируется, если у пользователя есть значение, то аутентифицируется.
firebaseConfig
можно найти в консоли Firebase => Настройки проекта => Приложения => Переключатель ConfigЭтот хук useEffect является ядром для отслеживания authChanges пользователя. Мы добавляем слушатель к событию onAuthStateChanged в firebase.auth (), который обновляет значение authUser. Этот метод возвращает обратный вызов для отмены подписки этого слушателя, который мы можем использовать для очистки слушателя при обновлении ловушки useFirebase.
Это единственный хук, который нам нужен для аутентификации в firebase (другие хуки могут быть сделаны для firestore и т. Д.
Это базовая реализация
App
компонентаcreate-react-app
Использование базы данных Hirestore Hook
Это может быть реализовано в вашем компоненте следующим образом:
}
Это тогда устраняет необходимость в React useContext, поскольку мы получаем обновления непосредственно от самой базы Firebase.
Обратите внимание на пару вещей:
Редактировать удалил большой кусок старого ответа
источник
Firebase не определен, потому что вы не импортируете его. Во-первых, он должен быть таким,
firebase.firestore()
как показано в примере на документах, которые вы связали с https://github.com/CSFrequency/react-firebase-hooks/tree/master/firestore . Затем вам нужно импортировать firebase в файл,import * as firebase from 'firebase';
как указано в readme пакета firebase https://www.npmjs.com/package/firebaseисточник
РЕДАКТИРОВАТЬ (3 марта 2020 г.):
Давайте начнем с нуля.
Я создал новый проект:
пряжа создать ответную реакцию
Я удалил все 3 файла App *, созданные по умолчанию, удалил импорт из index.js, а также удалил сервисного работника, чтобы получить чистый index.js вот так:
Давайте проверим, что мы можем прочитать что-нибудь из Firestore. Чтобы проверить только чтение, я пошел в свой проект в Firebase Console, открыл свою базу данных Cloud Firestore и добавил новую коллекцию с именем counters с простым документом, содержащим одно поле с именем value number и value 0.
Затем я обновил класс App для использования созданного нами FirebaseContext, сделал хук useState для нашего простого счетчика хуков и использовал хук useEffect для чтения значения из firestore:
Примечание: Чтобы сделать ответ как можно более коротким, я позаботился о том, чтобы вам не нужно было проходить аутентификацию с помощью firebase, установив доступ к firestore в тестовом режиме (файл firestore.rules):
Мой предыдущий ответ: не стесняйтесь взглянуть на мой response-firebase-auth-скелет:
https://github.com/PompolutZ/react-firebase-auth-skeleton
Это в основном следует за статьей:
https://www.robinwieruch.de/complete-firebase-authentication-react-tutorial
Но несколько переписано для использования хуков. Я использовал его как минимум в двух своих проектах.
Типичное использование из моего текущего любимого проекта:
Здесь две наиболее важные части: - ловушка useAuthUser (), которая предоставляет текущего аутентифицированного пользователя. - FirebaseContext, который я использую через ловушку useContext .
Когда у вас есть контекст для firebase, вы можете реализовать объект firebase по своему вкусу. Иногда я пишу некоторые полезные функции, иногда проще просто настроить слушателей прямо в хуке useEffect, который я создаю для моего текущего компонента.
Одной из лучших частей этой статьи было создание withAuthorization HOC, который позволяет указать предварительные условия для доступа к странице либо в самом компоненте:
Или, может быть, даже настройки этих условий прямо в реализации вашего маршрутизатора.
Надеюсь, что просмотр репо и статьи даст вам несколько хороших мыслей, чтобы улучшить другие блестящие ответы на ваш вопрос.
источник