रिएक्ट हुक के साथ फायरबेस श्रोता


27

मैं यह पता लगाने की कोशिश कर रहा हूं कि फायरबेस श्रोता का उपयोग कैसे किया जाए ताकि रिएक्शन हुक अपडेट के साथ क्लाउड फायरस्टार डेटा रीफ्रेश हो जाए।

प्रारंभ में, मैंने इसे फायरस्टार डेटा प्राप्त करने के लिए एक घटकडाइमाउंट फ़ंक्शन के साथ एक वर्ग घटक का उपयोग करके बनाया।

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 फ़ाइल है, जो इस प्रकार फायरस्टार में प्लग करता है:

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

फिर, मेरे फायरबेस संदर्भ सेटअप में मेरे पास है:

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;

तब, मेरे साथ HA के असंतोषन में, मेरे पास एक संदर्भ प्रदाता है:

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;

वर्तमान में - जब मैं यह कोशिश करता हूं, मुझे डैशबोर्ड 2 घटक में एक त्रुटि मिलती है जो कहती है:

फायरबेस ’परिभाषित नहीं है

मैंने फायरबेस को कम करने और उसी त्रुटि को प्राप्त करने की कोशिश की।

मैंने Firebase.firestore और Firebase.firestore भी आज़माया। मुझे समान त्रुटि मिली।

मैं सोच रहा था कि क्या मैं एक फ़ंक्शन घटक के साथ अपने HOC का उपयोग नहीं कर सकता हूं?

मैंने इस डेमो ऐप और इस ब्लॉग पोस्ट को देखा है ।

ब्लॉग में सलाह के बाद, मैंने एक नया फायरबेस / रेफरेंस बनाया।

 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 कोई फ़ंक्शन नहीं है।

मैंने इस पोस्ट को उस त्रुटि से निपटते देखा है और यार्न को अनइंस्टॉल करने और पुन: स्थापित करने का प्रयास किया है। इससे कोई फ़र्क नहीं पड़ता।

जब मैं डेमो ऐप को देखता हूं , तो यह सुझाव देता है कि 'इंटरफ़ेस' विधि का उपयोग करके संदर्भ बनाया जाना चाहिए। मैं यह नहीं देख सकता कि यह कहां से आ रहा है - मैं इसे प्रलेखन में समझाने के लिए एक संदर्भ नहीं पा सकता हूं।

मैं इसे प्लग इन करने के लिए मैंने क्या किया है, इसके अलावा अन्य निर्देशों की समझ नहीं बना सकता।

मैंने इस पोस्ट को देखा है जो प्रतिक्रिया-फायरबेस-हुक का उपयोग किए बिना फायरस्टार को सुनने का प्रयास करता है। जवाब इस उपकरण का उपयोग करने के तरीके का पता लगाने की कोशिश करते हैं।

मैंने इस उत्कृष्ट व्याख्या को पढ़ा है जो HOCs से हुक तक जाने का तरीका बताता है। मैं कैसे फायरबेस श्रोता को एकीकृत करने के साथ फंस गया हूँ।

मैंने इस पोस्ट को देखा है जो ऐसा करने के बारे में सोचने के लिए एक सहायक उदाहरण प्रदान करता है। निश्चित नहीं है कि क्या मुझे ऑरलिस्टस्टनर कंपोनेंटडीडमाउंट में ऐसा करने की कोशिश करनी चाहिए - या डैशबोर्ड घटक जो इसे इस्तेमाल करने की कोशिश कर रहा है।

NEXT ATTEMPT मैंने यह पोस्ट पाया , जो इसी समस्या को हल करने की कोशिश कर रहा है।

जब मैं शुभम खत्री द्वारा प्रस्तुत समाधान को लागू करने की कोशिश करता हूं, तो मैं फायरबेस कॉन्फ़िगरेशन को निम्नानुसार सेट करता हूं:

के साथ एक संदर्भ प्रदाता: 'प्रतिक्रिया' से आयात प्रतिक्रिया, {useContext}; आयात फायरबेस से '../../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' को '../irebase/ContextHookProvider' से निर्यात नहीं किया गया है।

वह त्रुटि संदेश समझ में आता है, क्योंकि ContextHookProvider FirebaseContext को निर्यात नहीं करता है - यह FirebaseProvider को निर्यात करता है - लेकिन अगर मैं डैशबोर्ड 2 में इसे आयात करने का प्रयास नहीं करता - तो मैं इसे उस फ़ंक्शन तक नहीं पहुंचा सकता जो इसे उपयोग करने का प्रयास नहीं करता है।

इस प्रयास का एक दुष्प्रभाव यह है कि मेरा साइन अप तरीका अब काम नहीं करता है। अब यह एक त्रुटि संदेश उत्पन्न करता है जो कहता है:

TypeError: null की संपत्ति 'doCreateUserWithEmailAndPassword' नहीं पढ़ सकता है

मैं इस समस्या को बाद में हल करूँगा- लेकिन फायरबेस के साथ प्रतिक्रिया का उपयोग करने के तरीके का पता लगाने का एक तरीका होना चाहिए जिसमें लाखों लूप के माध्यम से इस लूप के महीनों को शामिल नहीं किया जाता है जो एक बुनियादी स्थिति सेटअप प्राप्त करने के लिए काम नहीं करते हैं। क्या फायरबेस (फायरस्टार) के लिए स्टार्टर किट है जो प्रतिक्रिया हुक के साथ काम करता है?

अगला प्रयास मैंने इस udemy कोर्स में दृष्टिकोण का पालन करने की कोशिश की - लेकिन यह केवल एक फॉर्म इनपुट उत्पन्न करने के लिए काम करता है - प्रामाणिक उपयोगकर्ता के साथ समायोजित करने के लिए मार्गों के चारों ओर रखने के लिए कोई श्रोता नहीं है।

मैंने इस youtube ट्यूटोरियल में दृष्टिकोण का पालन करने की कोशिश की - जिसमें इस रेपो से काम करना है। यह दिखाता है कि हुक का उपयोग कैसे करें, लेकिन संदर्भ का उपयोग कैसे करें।

अगला ATTEMPT मुझे यह रेपो मिला जो लगता है कि फायरस्टार के साथ हुक का उपयोग करने के लिए एक अच्छा विचार है। हालाँकि, मैं कोड की समझ नहीं बना सकता।

मैंने इसे क्लोन किया - और सभी सार्वजनिक फ़ाइलों को जोड़ने की कोशिश की और फिर जब मैंने इसे चलाया - मुझे वास्तव में संचालित करने के लिए कोड नहीं मिल सकता है। मुझे यकीन नहीं है कि यह देखने के लिए निर्देशों से गायब नहीं है कि इसे कैसे चलाया जाए ताकि यह देखने के लिए कि क्या कोड में सबक हैं जो इस समस्या को हल करने में मदद कर सकते हैं।

अगले ATTEMPT

मैंने दिव्य टेम्पलेट खरीदा है, जिसे फायरबेस के लिए सेटअप के रूप में विज्ञापित किया गया है (यह फायरस्टार के लिए सेटअप नहीं है, अगर कोई और इसे विकल्प के रूप में विचार कर रहा है)।

वह टेम्पलेट एक एप्‍पल रैपर का प्रस्ताव करता है जो ऐप के कॉन्‍फ़िगरेशन को इनिशियलाइज़ करता है - लेकिन सिर्फ़ ऑर्कुटिकल मेथड्स के लिए - इसलिए इसे फायरस्टार के लिए एक अन्य संदर्भ प्रदाता की अनुमति देने के लिए रिस्ट्रक्चर करना होगा। जब आप उस प्रक्रिया से गुजरते हैं और इस पोस्ट में दिखाई गई प्रक्रिया का उपयोग करते हैं , तो जो कुछ बचा है वह निम्नलिखित कॉलबैक में त्रुटि है:

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

यह नहीं पता है कि फायरबेस क्या है। ऐसा इसलिए है क्योंकि इसे फायरबेस संदर्भ प्रदाता में परिभाषित किया गया है जिसे आयात और परिभाषित किया गया है (उपयोगप्रोवाइडऑथ () फ़ंक्शन) में निम्नानुसार है:

  const firebase = useContext(FirebaseContext)

कॉलबैक के अवसरों के बिना, त्रुटि कहती है:

रिएक्ट हुक का उपयोग करें एक लापता निर्भरता है: 'फायरबेस'। या तो इसे शामिल करें या निर्भरता सरणी को हटा दें

या, यदि मैं कॉलबैक में उस कास्ट को जोड़ने की कोशिश करता हूं, तो मुझे एक त्रुटि मिलती है जो कहती है:

रिएक्ट हुक "यूज़ कॉन्टेक्स्ट" को कॉलबैक के अंदर नहीं बुलाया जा सकता है। रिएक्ट हुक को रिएक्ट फ़ंक्शन घटक या एक कस्टम रिएक्ट हुक फ़ंक्शन में कहा जाना चाहिए

अगले ATTEMPT

मैंने अपनी फ़ायरबॉस् कॉन्फिगर फ़ाइल को केवल कॉन्फिगर वेरिएबल्स में घटा दिया है (मैं प्रत्येक संदर्भ के लिए संदर्भ प्रदाताओं में हेल्पर्स लिखूंगा जो मैं उपयोग करना चाहता हूं)।

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

मैंने एक फायरबेस संदर्भ प्रदाता भी इस प्रकार बनाया है:

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 में मैं ऐप को फायरबेस प्रदाता में लपेटता हूं

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

मैंने फायरबेस में पूंजीकृत एफ का उपयोग करने की कोशिश की है और यह उसी त्रुटि को उत्पन्न करता है।

जब मैं ट्रिस्टन की सलाह लेने की कोशिश करता हूं, तो मैं उन सभी चीजों को हटा देता हूं और अपने अनसब्सक्राइब विधि को एक अनलिस्ट विधि के रूप में परिभाषित करने की कोशिश करता हूं (मुझे नहीं पता कि वह फायरबेस भाषा का उपयोग क्यों नहीं कर रहा है - लेकिन अगर उसके दृष्टिकोण ने काम किया है, तो मैं और अधिक प्रयास करूंगा। यह जानने के लिए क्यों)। जब मैं उसके समाधान का उपयोग करने की कोशिश करता हूं, तो त्रुटि संदेश कहता है:

TypeError: _util_contexts_Firebase__WEBPACK_IMPORTED_MODULE_8 ___ डिफ़ॉल्ट (...) एक फ़ंक्शन नहीं है

इस पोस्ट का उत्तर ऑर्टिकल के बाद हटाने () से पता चलता है। जब मैं कोशिश करता हूं, तो मुझे एक त्रुटि मिलती है जो कहती है:

TypeError: अपरिभाषित की संपत्ति 'onAuthStateChanged' नहीं पढ़ सकता है

हालाँकि यह पोस्ट सुझाव फ़ाइल में जिस तरह से फायरबेस आयात किया गया है, के साथ एक समस्या का सुझाव देता है।

मेरे पास इसे आयात किया गया है: "../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" होना चाहिए, लेकिन यह अभी भी समस्या को हल करने के करीब कहीं भी नहीं मिलता है।

मैं अभी भी अटका हुआ हूं।

क्या कोई देख सकता है कि यहाँ क्या हो रहा है?


यह अपरिभाषित है क्योंकि आप इसे आयात नहीं कर रहे हैं।
जोश पिटमैन

3
क्या आपको बस जोड़ने की आवश्यकता exportहै export const FirebaseContext?
फेडरुन

हाय मेल धीमी प्रतिक्रिया के लिए माफी माँगता हूँ, मैं काम में एक पागल दो सप्ताह था तो शाम को एक कंप्यूटर पर देख सवाल से बाहर था! मैंने आपके लिए एक साफ और बहुत सरल समाधान प्रदान करने के लिए अपना उत्तर अपडेट किया है जिसे आप जांच सकते हैं।
ट्रिस्टन ट्रेनर

बहुत बहुत धन्यवाद - मैं इसे अब देखूंगा।
मेल

हे मेल, अभी फायरस्टार से रियल टाइम अपडेट के सही क्रियान्वयन के साथ ही इसे और अपडेट किया गया है (onSnapshot भाग को इसे वास्तविक समय नहीं बनाने के लिए हटा सकते हैं) यदि यह समाधान है, तो कृपया मुझे सुझाव है कि संभवतः इसे बनाने के लिए अपने प्रश्न को अपडेट करें बहुत कम और अधिक संक्षिप्त इसलिए इसे देखने वाले अन्य लोग भी समाधान पा सकते हैं, धन्यवाद - प्रतिक्रियाओं की धीमी प्रकृति के लिए फिर से खेद है
ट्रिस्टन ट्रेनर

जवाबों:


12

मेजर एडिट : इसे देखने के लिए कुछ समय लिया और यह वही है जो मैं लेकर आया हूं, यह एक क्लीनर समाधान है, कोई मुझसे इस बारे में असहमत हो सकता है कि यह एक अच्छा तरीका है।

UseFirebase प्रामाणिक हुक

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 }

यदि ऑक्टूसर शून्य है, तो प्रमाणित नहीं है, यदि उपयोगकर्ता का मूल्य है, तो प्रमाणित है।

firebaseConfigफायरबेस कंसोल पर पाया जा सकता है => प्रोजेक्ट सेटिंग्स => एप्स => रेडियो बटन को कॉन्फ़िगर करें

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

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

यह उपयोगक हूक एक उपयोगकर्ता के ऑर्किटेक्शंस को ट्रैक करने के लिए मुख्य है। हम firebase.auth () के onAuthStateChanged इवेंट में एक श्रोता को जोड़ते हैं जो ऑक्टाकोर के मान को अपडेट करता है। यह विधि इस श्रोता को अनसब्सक्राइब करने के लिए एक कॉलबैक लौटाती है जिसे हम उपयोग करने के लिए श्रोता को साफ करने के लिए उपयोग कर सकते हैं जब उपयोगफायरबेस हुक ताज़ा किया जाता है।

यह एकमात्र हुक है जो हमें फायरबेस प्रमाणीकरण के लिए चाहिए (अन्य हुक फायरस्टार आदि के लिए बनाया जा सकता है)

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

उपयोगफिरस्टोर डेटाबेस हुक

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

}

इसके बाद रिएक्ट यूज कॉन्टेक्स्ट की जरूरत को हटा दिया जाता है क्योंकि हमें सीधे फायरबेस से ही अपडेट मिलते हैं।

कुछ बातों पर ध्यान दें:

  1. अपरिवर्तित दस्तावेज़ को सहेजना एक नए स्नैपशॉट को ट्रिगर नहीं करता है, इसलिए "ओवरवेटिंग" रेंडरर्स का कारण नहीं बनता है
  2. कॉल करने पर getDocument कॉलबैक onUpdate को एक प्रारंभिक "स्नैपशॉट" के साथ सीधे कहा जाता है ताकि आपको दस्तावेज़ की प्रारंभिक स्थिति प्राप्त करने के लिए अतिरिक्त कोड की आवश्यकता न हो।

संपादित ने पुराने उत्तर का एक बड़ा हिस्सा निकाल दिया है


1
इसके लिए धन्यवाद। मैं नहीं देख सकता कि आपने इसे कैसे सेट किया है। प्रदाता के साथ, मुझे एक त्रुटि मिलती है जो कहती है कि createContext परिभाषित नहीं है। ऐसा इसलिए क्योंकि अभी तक कोई उपभोक्ता नहीं है। कहाँ रखा है तुम्हारा?
मेल

सॉरी क्रिएट क्रेटेक्स्ट प्रतिक्रिया का हिस्सा है, इसलिए इसे शीर्ष पर आयात करें {createContext} के रूप में 'रिएक्ट' से मैंने महसूस किया कि मैं यह दिखाना भूल गया कि फायरबेस प्रदाता कहाँ जाता है, मैं उत्तर संपादित करूँगा
ट्रिस्टन ट्रेनर

मैंने इसे प्रदाता में आयात किया था, लेकिन यह अपरिभाषित करता है। मुझे लगता है कि क्योंकि इसके लिए कोई उपभोक्ता नहीं है
मेल

1
उपभोक्ता यूज़ कॉन्टेक्स्ट () हुक है, लेकिन आपके प्रश्न को फिर से देख रहा है, ऐसा लगता है कि आप फ़ाइल से फायरबैसकोटेक्स्ट को निर्यात नहीं कर रहे हैं - यही कारण है कि यह संदर्भ नहीं पा रहा है :)
ट्रिस्टन ट्रेनर

1
हाय @Mel आपको बहुत बहुत धन्यवाद, मुझे आशा है कि यह अंत में मददगार साबित होगा। हुक और फायरबेस दोनों काफी शामिल हैं और मुझे इसके चारों ओर अपना सिर प्राप्त करने के लिए एक लंबा समय लगा और मुझे अब भी सबसे अच्छा समाधान नहीं मिला है, मैं अपने दृष्टिकोण पर कुछ समय के लिए एक ट्यूटोरियल बना सकता हूं क्योंकि आपको कोड के रूप में समझाना आसान है यह।
ट्रिस्टन ट्रेनर

4

Firebase अपरिभाषित है क्योंकि आप इसे आयात नहीं कर रहे हैं। सबसे पहले, इसकी जरूरत है firebase.firestore()कि उदाहरण में दिखाए गए डॉक्स पर आप https://github.com/CSFrequency/react-firebase-hooks/tree/master/firestore से जुड़े हैं । फिर आपको फ़ाइल में वास्तव में फायरबेस आयात करने की आवश्यकता है, इसलिए import * as firebase from 'firebase';फायरबेस पैकेज रीडमी https://www.npmjs.com/package/firebase में उल्लिखित


1
मैं इसे index.js में आयात कर रहा हूं
मेल

1
ReactDOM.render (<FirebaseContext.Provider value = {new Firebase ()}> <App /> </FirebaseContext.Provider>, document.getElementByDd ('root'));
मेल

1
यही कारण है कि दृष्टिकोण कंपोनेंटडाउन के साथ काम करता है
मेल

1
WithAuth HOC भी withFirebase में लिपटी है।
मेल

3
लेकिन आपकी त्रुटि कहती है कि यह अपरिभाषित है। मैं आपको इसे परिभाषित करने में मदद कर रहा हूं, और आप मुझे यह नहीं बता रहे हैं कि समाधान काम करता है या परिणामी त्रुटि क्या है। आप हमेशा मेल को मदद करने के लिए इसे बहुत कठिन बनाते हैं। मेरा कहना है कि आप इसे डैशबोर्ड 2 घटक फ़ाइल के अलावा किसी फ़ाइल में आयात कर रहे हैं, जहाँ आप इसे संदर्भित करते हैं, जो त्रुटि पैदा कर रहा है। अनुक्रमणिका में किसी चीज़ को शामिल करना आपके निर्माण को समझने में मदद नहीं करता है कि यह पूरी तरह से अलग फ़ाइल में क्या है।
जोश पिटमैन

2

EDIT (3 मार्च, 2020):

चलो खरोंच से शुरू करते हैं।

  1. मैंने एक नई परियोजना बनाई है:

    यार्न रिएक्शन-ऐप फायरबेस-हुक-इश्यू बनाते हैं

  2. मैंने डिफ़ॉल्ट रूप से बनाई गई सभी 3 ऐप * फ़ाइलों को हटा दिया है, 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. मैंने यह देखने के लिए ऐप शुरू किया है कि हैलो फायरबेस! छपा हुआ है।
  2. मैंने फायरबेस मॉड्यूल जोड़ा है
yarn add firebase
  1. मैंने उस प्रोजेक्ट के लिए फायरबेस सेटअप करने के लिए फायरबेस इनिट चलाया है। मैंने अपनी खाली फायरबेस परियोजनाओं में से एक को चुना है और मैंने डेटाबेस और फायरस्टार का चयन किया है, जो अगली फाइलें बनाने में समाप्त होता है:
.firebaserc
database.rules.json
firebase.json
firestore.indexes.json
firestore.rules
  1. मैंने फायरबेस लिबास के लिए आयात जोड़ा है और एक फायरबेस क्लास और फायरबेससीकोटेक्स्ट भी बनाया है । अंत में मैंने 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. आइए सत्यापित करें कि हम फायरस्टार से कुछ भी पढ़ सकते हैं। पहले केवल पढ़ने को सत्यापित करने के लिए, मैं फायरबेस कंसोल में अपने प्रोजेक्ट पर गया, अपना क्लाउड फायरस्टार डेटाबेस खोला और एक नए संग्रह को जोड़ा जिसमें काउंटर नाम का एक दस्तावेज़ सरल था जिसमें एक फ़ील्ड टाइप संख्या और मान 0 का मान कहा गया था । यहां छवि विवरण दर्ज करें यहां छवि विवरण दर्ज करें

  2. फिर मैंने ऐप क्लास को हमारे द्वारा बनाए गए FirebaseContext का उपयोग करने के लिए अपडेट किया है, हमारे साधारण काउंटर हुक के लिए यूज़स्टेट हुक का उपयोग किया है और फायरस्टार से मूल्य पढ़ने के लिए उपयोग हुक का उपयोग किया है:

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.rules फ़ाइल) में सेट करके:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {

    // This rule allows anyone on the internet to view, edit, and delete
    // all data in your Firestore database. It is useful for getting
    // started, but it is configured to expire after 30 days because it
    // leaves your app open to attackers. At that time, all client
    // requests to your Firestore database will be denied.
    //
    // Make sure to write security rules for your app before that time, or else
    // your app will lose access to your Firestore database
    match /{document=**} {
      allow read, write: if request.time < timestamp.date(2020, 4, 8);
    }
  }
}

मेरा पिछला उत्तर: मेरी प्रतिक्रिया-फायरबेस-स्केलेटन पर एक नज़र डालने के लिए आपका स्वागत है।

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 () हुक जो वर्तमान प्रमाणित उपयोगकर्ता प्रदान करता है। - FirebaseContext जो मैं के माध्यम से उपयोग करने useContext हुक।

const firebase = useContext(FirebaseContext);

जब आपके पास फायरबैस का संदर्भ होता है, तो आपकी पसंद के हिसाब से फायरबेस ऑब्जेक्ट को लागू करने के लिए आप तक। कभी-कभी मैं कुछ सहायक कार्यों को लिखता हूं, कभी-कभी इसका आसान उपयोग केवल श्रोताओं को सीधे करने के लिए होता है उपयोग हुक में मैं अपने वर्तमान घटक के लिए बनाता हूं।

उस लेख के सबसे अच्छे हिस्सों में से एक withAuthorization HOC का निर्माण था , जो आपको पृष्ठ को घटक में या तो एक्सेस करने के लिए आवश्यक शर्तें निर्दिष्ट करने की अनुमति देता है:

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

या हो सकता है कि आपके राउटर क्रियान्वयन में भी वे स्थितियां ठीक हों।

आशा है कि रेपो और लेख को देखकर आपको अपने प्रश्न के अन्य शानदार उत्तर बढ़ाने के लिए कुछ अतिरिक्त अच्छे विचार मिलेंगे।


मैंने उनकी किताब खरीदी और उनके दृष्टिकोण का अनुसरण किया। मैंने पाया कि लागू होने के दौरान स्थितियाँ दृष्टिकोण वास्तव में काम नहीं करती थीं और उस पुस्तक में सेट किए गए ऑर्टिकल प्रोटोकॉल घटक अद्यतन के माध्यम से स्थिति बनाए रखने में विफल रहे। मुझे उस पुस्तक का उपयोग करने का कोई तरीका नहीं मिला है। वैसे भी अपने विचार साझा करने के लिए धन्यवाद।
मेल

मुझे स्पष्ट नहीं है आपका क्या मतलब है। क्या आपने अपने फायरबेस प्रोजेक्ट के साथ मेरा कंकाल ऐप आज़माया है? जहां तक ​​मैं बता सकता हूं, सभी स्थितियां काम करती हैं, तो coz मैं कम से कम 3 परियोजनाओं में इसका उपयोग कर रहा हूं।
fxdxpz
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.