Я пытаюсь понять, как использовать прослушиватель 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
?