[javascript] 반응 후크가있는 Firebase 리스너

Firebase 리스너를 사용하여 클라우드 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;

그런 다음 with with Authorization 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는 함수가 아닙니다

내가 본 이 게시물 이 오류 처리 및 제거하고 실을 다시 설치 시도했다. 차이가 없습니다.

데모 앱을 살펴보면 ‘인터페이스’방법을 사용하여 컨텍스트를 만들어야한다고 제안합니다. 이것이 어디에서 왔는지 알 수 없습니다-설명서에서 설명하는 참조를 찾을 수 없습니다.

나는 이것을 연결하기 위해 한 것을 시도하는 것 이외의 지시를 이해할 수 없다.

react-firebase-hook을 사용하지 않고 firestore를 청취하려는 이 게시물 을 보았습니다 . 답은이 도구를 사용하는 방법을 알아 내려고하는 것입니다.

HOC에서 후크로 이동하는 방법에 대한 훌륭한 설명 을 읽었습니다 . Firebase 리스너를 통합하는 방법에 갇혀 있습니다.

나는이 일을 생각하는 방법에 대한 유용한 예를 제공하는 이 게시물 을 보았습니다 . authListener componentDidMount 또는이를 사용하려는 대시 보드 구성 요소에서이 작업을 수행해야하는지 확실하지 않습니다.

다음 공격
나는 이 게시물을 찾았 는데, 같은 문제를 해결하려고합니다.

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>
); 

컨텍스트 후크는 다음과 같습니다.

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} />

이것을 시도하면 다음과 같은 오류가 발생합니다.

가져 오기 오류가 발생했습니다 : ‘../Firebase/ContextHookProvider’에서 ‘FirebaseContext’를 내 보내지 않았습니다.

이 오류 메시지는 ContextHookProvider가 FirebaseContext를 내 보내지 않기 때문에-FirebaseProvider를 내 보내지 만-Dashboard2에서 가져 오려고 시도하지 않으면 사용하려고하는 함수에서 액세스 할 수 없습니다.

이 시도의 부작용 중 하나는 가입 방법이 더 이상 작동하지 않는다는 것입니다. 이제 다음과 같은 오류 메시지가 생성됩니다.

TypeError : null의 ‘doCreateUserWithEmailAndPassword’속성을 읽을 수 없습니다

나중에이 문제를 해결할 것입니다.하지만 기본 인증 설정을 수행 할 수없는 수백만 번의 경로를 통해이 루프를 수개월 동안 수행하지 않는 firebase와 반응하는 방법을 알아낼 수있는 방법이 있어야합니다. 반응 고리와 작동하는 firebase (firestore) 용 스타터 키트가 있습니까?

다음 시도
는이 udemy 과정 의 접근법을 따르려고 시도 했지만 양식 입력 만 생성합니다. 인증 된 사용자와 조정하기 위해 경로 주위에 리스너가 없습니다.

YouTube 자습서 의 접근 방식을 따르려고했습니다 . 이 레포 가 작동합니다. 후크를 사용하는 방법을 보여 주지만 컨텍스트를 사용하는 방법은 보여주지 않습니다.

다음 공격
나는 소방서에 후크를 사용하는 방법을 잘 알고있는 이 레포 를 발견 했습니다 . 그러나 코드를 이해할 수 없습니다.

나는 이것을 복제하고 모든 공개 파일을 추가하려고 시도한 다음 실행할 때 실제로 코드를 작동시킬 수 없습니다. 이 문제를 해결하는 데 도움이되는 코드에 교훈이 있는지 확인하기 위해이를 실행하는 방법에 대한 지침에서 누락 된 것이 무엇인지 잘 모르겠습니다.

다음 공격

나는 divjoy 템플릿을 구입했는데 firebase에 대한 설정으로 광고됩니다 (다른 사람이 이것을 옵션으로 고려할 경우 firestore에 대해서는 설정되지 않았습니다).

이 템플릿은 앱의 구성을 초기화하는 인증 래퍼를 제안하지만 인증 메소드에 대해서만 다른 컨텍스트 제공자가 firestore를 사용할 수 있도록 재구성해야합니다. 해당 프로세스를 방해 하고이 게시물에 표시된 프로세스를 사용하면 남은 것은 다음 콜백의 오류입니다.

useEffect(() => {
    const unsubscribe = firebase.auth().onAuthStateChanged(user => {
      if (user) {
        setUser(user);
      } else {
        setUser(false);
      }
    });

Firebase가 무엇인지 알 수 없습니다. 이는 (proProdedeAuth () 함수에서) 가져오고 정의 된 firebase 컨텍스트 제공자에 다음과 같이 정의되어 있기 때문입니다.

  const firebase = useContext(FirebaseContext)

콜백 기회가 없으면 오류는 다음과 같이 표시됩니다.

React Hook useEffect의 종속성이 누락되었습니다 : ‘firebase’. 포함 시키거나 종속성 배열을 제거하십시오.

또는 콜백에 해당 const를 추가하려고하면 다음과 같은 오류가 발생합니다.

콜백 내에서 React Hook “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);
      }
    });

Firebase에서 대문자 F를 사용하려고 시도했지만 동일한 오류가 발생합니다.

Tristan의 조언을 시도 할 때 모든 것을 제거하고 수신 거부 방법을 비공개 방법으로 정의하려고 시도합니다 (왜 그가 firebase 언어를 사용하지 않는지 모르겠지만 그의 접근 방식이 효과가 있다면 더 열심히 노력할 것입니다) 이유를 파악하기 위해). 그의 솔루션을 사용하려고하면 오류 메시지가 나타납니다.

TypeError : _util_contexts_Firebase__WEBPACK_IMPORTED_MODULE_8 ___ default (…)는 함수가 아닙니다

이 게시물에 대한 답변은 인증 후 ()를 제거 하도록 제안합니다. 시도하면 다음과 같은 오류가 발생합니다.

TypeError : 정의되지 않은 ‘onAuthStateChanged’속성을 읽을 수 없습니다

그러나이 게시물 은 auth 파일에서 firebase를 가져 오는 방식에 문제가 있음을 나타냅니다.

“../firebase”에서 import Firebase;

Firebase는 클래스 이름입니다.

Tristan이 권장하는 비디오는 유용한 배경이지만, 현재 에피소드 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>
  );  

나는이 우디 코스 를 사용 하여 컨텍스트를 파악 하고이 문제에 대한 요소를 파악하려고 노력했습니다.이를 본 후 아래 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가 널이면 인증되지 않고, 사용자에게 값이 있으면 인증됩니다.

firebaseConfigfirebase 콘솔 => 프로젝트 설정 => => 구성 라디오 버튼 에서 찾을 수 있습니다

useEffect(() => {
  const unsubscribe = firebase.auth()
    .onAuthStateChanged(setAuthUser)

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

이 useEffect 후크는 사용자의 인증 변경을 추적하는 핵심입니다. authUser의 값을 업데이트하는 fireAuth.auth ()의 onAuthStateChanged 이벤트에 리스너를 추가합니다. 이 메소드는 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>
}

이것은 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 }
}

이것은 당신의 컴포넌트에서 다음과 같이 구현 될 수 있습니다 :

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가 필요하지 않습니다.

몇 가지 사항에 주목하십시오.

  1. 변경되지 않은 문서를 저장해도 새 스냅 샷이 트리거되지 않으므로 “과도하게 저장”해도 다시 렌더링되지 않습니다
  2. getDocument를 호출하면 초기 “스냅 샷”과 함께 onUpdate 콜백이 즉시 호출되므로 문서의 초기 상태를 얻는 데 추가 코드가 필요하지 않습니다.

편집은 이전 답변의 큰 덩어리를 제거했습니다


답변

Firebase는 가져 오지 않기 때문에 정의되지 않았습니다. 먼저 https://github.com/CSFrequency/react-firebase-hooks/tree/master/firestore에firebase.firestore() 연결된 문서의 예제에 표시된 것과 같아야합니다 . 그런 다음 firebase 패키지 readme https://www.npmjs.com/package/firebase에 설명 된대로 실제로 파일에서 firebase를 가져와야합니다.import * as firebase from 'firebase';


답변

편집 (2020 년 3 월 3 일) :

처음부터 시작합시다.

  1. 새 프로젝트를 만들었습니다.

    털실은 반응 앱 파이어베이스 후크 문제를 만듭니다

  2. 기본적으로 생성 된 3 개의 App * 파일을 모두 삭제하고 index.js에서 가져 오기를 제거했으며 서비스 작업자를 제거하여 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'));
  1. Hello Firebase 를 확인하기 위해 앱을 시작했습니다 !인쇄됩니다.
  2. Firebase 모듈을 추가했습니다
yarn add firebase
  1. 해당 프로젝트의 firebase를 설정하기 위해 firebase init를 실행했습니다. 빈 firebase 프로젝트 중 하나를 선택했으며 다음 파일을 생성하는 데이터베이스 및 Firestore를 선택했습니다.
.firebaserc
database.rules.json
firebase.json
firestore.indexes.json
firestore.rules
  1. firebase 라이브러리에 대한 가져 오기를 추가하고 Firebase 클래스 및 FirebaseContext 도 만들었습니다 . 마지막으로 FirebaseContext.Provider 구성 요소 에서 앱을 래핑 하고 값을 새 Firebase () 인스턴스로 설정했습니다. 단일 인스턴스 여야하므로 Firebase 앱 인스턴스를 하나만 인스턴스화해야했습니다.
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"));
  1. Firestore에서 무엇이든 읽을 수 있는지 확인합시다. 첫번째 단지 읽기를 확인하기 위해, 나는 중포 기지 콘솔에서 내 프로젝트라는 새 컬렉션 내 클라우드 경우 FireStore 데이터베이스를 열고 추가에 갔다 카운터 문서와 간단한 라는 하나 개의 필드에 포함 된 유형 번호와 값 0을.
    여기에 이미지 설명을 입력하십시오
    여기에 이미지 설명을 입력하십시오

  2. 그런 다음 생성 한 FirebaseContext를 사용하도록 App 클래스를 업데이트하고 간단한 카운터 후크에 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"));

참고 : 답변을 최대한 짧게 유지하려면 firestore에 대한 액세스를 테스트 모드 (firestore.rules 파일)로 설정하여 firebase로 인증 할 필요가 없도록했습니다.

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);
    }
  }
}

내 이전 답변 :
당신은 내 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>
    );
}

가장 중요한 두 부분은 다음과 같습니다.- 현재 인증 된 사용자를 제공하는 useAuthUser () 후크. -useContext 후크 를 통해 사용하는 FirebaseContext .

const firebase = useContext(FirebaseContext);

firebase에 대한 컨텍스트가 있으면 원하는대로 firebase 객체를 구현해야합니다. 때로는 유용한 기능을 작성하고 때로는 현재 구성 요소에 대해 사용 하는 useEffect 후크 에서 리스너를 설정하기가 더 쉽습니다 .

이 기사의 가장 좋은 부분 중 하나는 withAuthorization HOC를 작성하는 것인데 ,이를 통해 컴포넌트 자체에서 페이지에 액세스하기위한 전제 조건을 지정할 수 있습니다.

const condition = authUser => authUser && !!authUser.roles[ROLES.ADMIN];
export default withAuthorization(condition)(AdminPage);

또는 라우터 구현에서 바로 이러한 조건을 설정할 수도 있습니다.

리포지토리와 기사를 보면 질문에 대한 다른 훌륭한 답변을 향상시킬 수있는 좋은 생각을 얻을 수 있기를 바랍니다.


답변