Я намагаюся розібратися, як використовувати слухач Firebase, щоб дані хмарного firestore були оновлені оновленнями реактивних гаків.
Спочатку я робив це, використовуючи компонент класу з функцією компонентDidMount, щоб отримати дані 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,
});
}
Це руйнується, коли сторінка оновлюється, тому я намагаюся зрозуміти, як перенести слухача на реакцію на гачки.
Я встановив інструмент " гаки-гачки" , хоча я не можу зрозуміти, як прочитати інструкції, щоб змусити його працювати.
У мене є функціональний компонент наступним чином:
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);
Цей компонент загортається в обгортку авторизації на рівні маршруту таким чином:
<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();
}
Він також визначає слухача, який повинен знати, коли змінюється автор:
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 };
Контекст налаштовується в обгорткуFirebase наступним чином:
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;
Тоді в моєму інструменті з аутентифікацією 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 не є функцією
Я бачив, як ця публікація займається цією помилкою і спробувала видалити та перевстановити пряжу. Це не має ніякої різниці.
Коли я дивлюся на демонстраційну програму , це говорить про те, що контекст слід створювати за допомогою методу "інтерфейс". Я не бачу, звідки це походить - я не можу знайти посилання, щоб пояснити це в документації.
Я не можу мати сенсу інших інструкцій, крім того, щоб спробувати те, що я зробив, щоб підключити цю програму.
Я бачив цю публікацію, яка намагається слухати firestore без використання гаків react-firebase. Відповіді вказують на спробу розібратися, як користуватися цим інструментом.
Я прочитав це чудове пояснення, яке стосується того, як перейти від ГОС до гачків. Я застряг у тому, як інтегрувати слухач Firebase.
Я бачив цю публікацію, яка є корисним прикладом, як думати про це. Не впевнений, чи потрібно мені намагатися це зробити в authListener компонентDidMount - або в компоненті панелі інструментів, який намагається це використовувати.
НАЙКРАЩА АТЕМПТ Я знайшов цю посаду , яка намагається вирішити ту саму проблему.
Коли я намагаюся реалізувати рішення, запропоноване Shubham Khatri, я налаштував конфігурацію firebase так:
Контекстний постачальник з: import React, {useContext} з '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;
Потім я вказую додаток у провайдера як:
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, яка не передбачає місяці цього циклу через мільйони проспектів, які не працюють, щоб отримати базову настройку авторизації. Чи є стартовий комплект для пожежної бази (firestore), який працює з гачками реагування?
Наступна спроба я спробував дотримуватися підходу в цьому курсі udemy - але це працює лише для формування вводу форми - немає слухача, який би розміщував маршрути для налаштування з автентифікованим користувачем.
Я намагався дотримуватися підходу в цьому навчальному посібнику ютуб - з якого над цим репо потрібно працювати. Він показує, як використовувати гачки, але не як використовувати контекст.
НАСТУПНИЙ НАСТУП Я знайшов це репо, яке, здається, має добре продуманий підхід до використання гачків з вогнем. Однак я не можу зрозуміти код.
Я клонував це - і спробував додати всі загальнодоступні файли, а потім, коли його запускаю - насправді не можу отримати код для роботи. Я не впевнений, чого не вистачає в інструкціях, як змусити це запустити, щоб перевірити, чи є уроки в коді, які можуть допомогти вирішити цю проблему.
НАСЛІДЧИЙ НАСТУП
Я купив шаблон divjoy, який рекламується як налаштування для firebase (це не налаштування для firestore у випадку, якщо хтось інший розглядає це як варіант).
Цей шаблон пропонує обгортку автентичності, яка ініціалізує конфігурацію програми, але лише для методів аутентифікації, тому її потрібно реструктурувати, щоб дозволити інший постачальник контексту для firestore. Коли ви заблукаєте через цей процес і використовуєте процес, показаний у цій публікації , що залишилось - це помилка в наступному зворотному дзвінку:
useEffect(() => {
const unsubscribe = firebase.auth().onAuthStateChanged(user => {
if (user) {
setUser(user);
} else {
setUser(false);
}
});
Він не знає, що таке firebase. Це тому, що він визначений у контексті постачальника Firebase, який імпортується та визначається (у функції useProvideAuth ()) як:
const firebase = useContext(FirebaseContext)
Без шансів на зворотний виклик помилка говорить:
React Hook useEffect має відсутність залежності: "firebase". Або включіть його, або видаліть масив залежності
Або якщо я спробую додати цей const до зворотного дзвінка, я отримаю помилку, яка говорить:
Не можна викликати внутрішній зворотний дзвінок "useContext" React Hook. Реактивні гачки повинні викликатися в компоненті функції 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;
Потім у мене є постачальник аутентичних контекстів таким чином:
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);
}
});
Я спробував використовувати з великої літери F у Firebase, і це генерує ту ж помилку.
Коли я спробую поради Тристана, я видаляю всі ці речі і намагаюсь визначити свій метод відписки як метод "unlisten" (я не знаю, чому він не використовує мову firebase - але якщо його підхід спрацював, я б постарався більше розібратися, чому). Коли я намагаюся використовувати його рішення, повідомлення про помилку говорить:
TypeError: _util_contexts_Firebase__WEBPACK_IMPORTED_MODULE_8 ___ за замовчуванням (...) не є функцією
Відповідь на це повідомлення пропонує видалити () з моменту авт. Коли я намагаюся це, я отримую помилку, яка говорить:
TypeError: Неможливо прочитати властивість "onAuthStateChanged" невизначеного
Однак ця публікація передбачає проблему із способом імпортування firebase у файл auth.
У мене це імпорт як: імпорт Firebase з "../firebase";
Firebase - це назва класу.
Відео, рекомендовані Трістаном, є корисним фоном, але зараз я перебуваю в епізоді 9, і досі не знайдено тієї частини, яка повинна допомогти вирішити цю проблему. Хтось знає, де це знайти?
Наступна ATTEMPT Далі - і намагаюся вирішити лише контекстну проблему - я імпортував і 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
?