RxFire в ReactJS с использованием Firebase Firestore и аутентификации

RxFire был создан Дэвидом Истом, который является Firebase Developer Advocate. Хотя Firebase Blog представил RxFire в сентябре 2018 года, я подумал, что это будет хорошим ориентиром для новичков, начинающих работать с ReactJs и Firebase.Для тех, кто пришел из Angular, вы можете быть знакомы с RxJS и пакетом Angularfire2. Из своего опыта я знаю, что вы ДОЛЖНЫ изучить RxJS, если собираетесь использовать Angular в приложении любого размера. Это может быть одной из самых трудных частей для изучения, но есть несколько фантастических учебников и сайтов, посвященных тому, как работает RxJS.

Новичок в React, пожалуйста, ознакомьтесь

Пример

В примере ниже показан быстрый предварительный просмотр того, как будет работать конечное приложение React. Как вы можете видеть, есть 4 этапа, которые будут происходить.

  • Войти

  • Добавить факт о кошке

  • Показать добавленный факт о кошке в списке

  • Выйти

Установка

Вам понадобится NPM, который поставляется в комплекте с nodejs. Поскольку мы будем использовать create-react-app cli, вы должны сначала установить его.

Вам также нужно будет создать свой собственный проект Firebase и инициализировать базу данных Firestore, пожалуйста, создайте ее, используя строгий режим.

Окончательная структура приложения

Установите зависимости

Вам понадобятся rxfire и firebase.

npm i rxfire firebase

Создайте Firebase.js

Firebase.js будет нашим основным файлом Firebase и будет включать инструменты для нашего приложения firebase, включая инстанцирование firebase. Этот файл включает rxfire для вспомогательных функций auth и firestore.Firebase.js

«`plain text
import ‘firebase/auth’;
import ‘firebase/firestore’;
import firebase from ‘firebase/app’;
import { authState } from ‘rxfire/auth’;
import { collectionData } from ‘rxfire/firestore’;
import { filter } from ‘rxjs/operators’;
const app = firebase.initializeApp({ /* Поместите сюда свою конфигурацию */ });
const firestore = firebase.firestore(app); //Инициализация firestore
const auth = firebase.auth(app); //Инициализация firebase auth
const loggedIn$ = authState(auth).pipe(filter(user => !!user)); //Наблюдаемая возвращается только тогда, когда пользователь вошел в систему.
export { app, auth, firestore, collectionData, loggedIn$ };
export default firebase;




> You can skip the Instructions part of the lesson if you are already familiar with ReactJS.

## Simple Initial Component

### Remove App.js and replace

Remove the core of App.js to start as we will be using the pattern of `extends React.Component`. We will now start simple by adding our first React Component Called Instructions.



```typescript
import "./App.css";
import React from "react";
import Instructions from "./components/Instructions";
class App extends React.Component {
  render() {
    return (
      <div
        style={{
          display: "flex",
          justifyContent: "center",
          flexFlow: "row wrap",
        }}
      >
        {" "}
        <div
          style={{
            display: "flex",
            justifyContent: "center",
            flexFlow: "column",
          }}
        >
          {" "}
          <Instructions />{" "}
        </div>{" "}
      </div>
    );
  }
}
export default App;

Вход в полноэкранный режим Выход из полноэкранного режима

Создание Instructions.js

Это очень простой React Component, который не принимает никаких props, он просто возвращает прямой html.components/Instructions.js.

import React from "react";
class Instructions extends React.Component {
  render() {
    return (
      <div
        style={{
          display: "flex",
          justifyContent: "start",
          flexFlow: "row wrap",
        }}
      >
        {" "}
        <div style={{ maxWidth: "500px" }}>
          {" "}
          <img
            src="https://res.cloudinary.com/ajonp/image/upload/q_auto/ajonp-ajonp-com/17-rxfire-react-cats/RxFire_3.webp"
            alt="rxfire for cats"
            width="100%"
          />{" "}
        </div>{" "}
        <div>
          {" "}
          <p>
            {" "}
            In this example we will use RxFire to Observe the Cat Facts that we
            add to our Firestore Database.{" "}
          </p>{" "}
          <a
            href="https://github.com/AJONPLLC/rxfire-react-cats"
            target="no_index"
          >
            {" "}
            https://github.com/AJONPLLC/rxfire-react-cats{" "}
          </a>{" "}
          <ol>
            {" "}
            <li>
              {" "}
              Sign In{" "}
              <ul>
                {" "}
                <li>Uses Google Auth for Firebase</li> <li>Found in App.js</li>{" "}
              </ul>{" "}
            </li>{" "}
            <li>
              {" "}
              Add Cat Fact{" "}
              <ul>
                {" "}
                <li>
                  {" "}
                  This will use an API and Insert the facts into Firestore{" "}
                </li>{" "}
                <li>Found in components/AddCat.js</li>{" "}
              </ul>{" "}
            </li>{" "}
            <li>
              {" "}
              Firestore collection{" "}
              <ul>
                {" "}
                <li>
                  {" "}
                  Observing catfacts for changes, heart eyes are your facts{" "}
                </li>{" "}
                <li>Found in components/ListCatFacts.js</li>{" "}
              </ul>{" "}
            </li>{" "}
            <li>
              {" "}
              Sign Out{" "}
              <ul>
                {" "}
                <li>Observe that user is removed</li>{" "}
              </ul>{" "}
            </li>{" "}
          </ol>{" "}
        </div>{" "}
      </div>
    );
  }
}
export default Instructions;

Вход в полноэкранный режим Выйти из полноэкранного режима

Обновление коллекции catfacts

Создайте AddCat.js

Первая кнопка, которую мы собираемся добавить, достаточно проста, она вызывает API и заталкивает возвращенные данные в коллекцию firestore как новый документ. Мне всегда нравится работать сверху вниз, поэтому сначала импортируем AddCat.js в наш App.js.

App.js

... import AddCat from './components/AddCat'; ... <div style={{ maxWidth: '800px' }}> <AddCat /> </div> ...
Вход в полноэкранный режим Выход из полноэкранного режима

Теперь, когда он находится в нашем App, давайте создадим компонент AddCat. Единственный html, который он передает обратно, это onClick={this.addCatFact}. Это JSX способ использования onClick и передачи функции. Если вы новичок в стрелочных функциях ES6 addCatFact = () => { — это все еще просто определение функции с использованием сокращенного синтаксиса, который становится все более стандартным в наши дни.Функция addCatFact вызывает API, который возвращает один случайный факт о кошке из https://cat-fact.herokuapp.com. Из-за CORS мы должны передать его через прокси, но вы увидите, что мы получим значение обратно. Затем мы используем функцию деструктуризации javascript для создания нового объекта, добавив к нему catFactDate. Это позволит нам в дальнейшем сортировать факты в нашем списке по убыванию

order.components/AddCat.js

import React from 'react';
import { firestore } from '../Firebase';
class AddCat extends React.Component {
    addCatFact = () => {
        /* The dreaded CORS, had to pass through a proxy */
        fetch(`https://cors-anywhere.herokuapp.com/https://cat-fact.herokuapp.com/facts/random?animal_type=cat&amount=1`)
            .then(blob => blob.json())
            .then(value => {
                console.log('fetched', value);
                firestore.collection('catfacts')
                    .add({ ...value, catFactDate: new Date() })
                    .then(() => { }, reason => {
                        alert('Must Be Logged In To Add, See Console');
                        console.log('Failed Adding Cat Fact', reason);
                    });
            });
    };
    render() {
        return (
            <button className="myButton" onClick={this.addCatFact}> 2. Add Cat Fact </button>
        );
    }
}
export default AddCat;
Вход в полноэкранный режим Выйти из полноэкранного режима

Если вы попробуете сделать это прямо сейчас, то это должно привести к неудаче с таким предупреждением

Это ожидаемо, так как наши правила firestore.rules установлены в строгий режим, мы добавим их после того, как пройдем через раздел аутентификации. Вы также можете заметить в консоли (и на экране, где факт прыгает туда-сюда), что firebase действительно добавляет в наш массив, пока не произойдет сбой на бэкенде. Это сделано специально, так как это дает нам быстрый пользовательский интерфейс, который мы ожидаем, сохраняя при этом целостность данных.

Добавьте аутентификацию Firebase

Нам нужно хранить состояние в нашем приложении, поэтому мы можем определить объект state с полем user, которое мы будем использовать из firebase, когда пользователь входит в систему. Мы также импортируем новую кнопку SignIn, которую мы можем использовать для скрытия показа Sign In / Sign Out в зависимости от текущего состояния. В компоненте SignIn мы впервые начнем использовать props, мы передадим состояние пользователя, а также пару наших функций, которые необходимы в компоненте для обновления нашего user.App.js.

... import { firestore, loggedIn$ } from './Firebase'; ...
state = { user: null }; componentDidMount() {
    /* Observable from RxFire */
    loggedIn$.subscribe(user => { this.authHandler({ user });
    //Update state on load of app
    const { displayName, email, phoneNumber, photoURL } = user;
    firestore .collection('users') .doc(user.uid)
    .set({ displayName, email, phoneNumber, photoURL });
    }); }
    authHandler = async authData => {
        this.setUser(authData.user); };
        setUser = user => {
            this.setState({ user: user }); };
            ...
            <Instructions />
            <SignIn user={this.state.user} authHandler={this.authHandler} setUser={this.setUser} />
            <div style={{ maxWidth: '800px' }}>
            <AddCat user={this.state.user} />
            ...

Вход в полноэкранный режим Выход из полноэкранного режима

SignIn.js

«простой текст
import React from «react»;
import firebase, { app } from «../Firebase»;
class SignIn extends React.Component {
signIn = () => {
const authProvider = new firebase.auth.GoogleAuthProvider();
app.auth().signInWithPopup(authProvider).then(this.props.authHandler);
};
signOut = async () => {
await firebase.auth().signOut();
this.props.setUser(null);
};
render() {
if (!this.props.user) {
return (

{» «}
1. Sign In{» «}

);
} else {
return (

{» «}
Welcome {this.props.user.email} {» «}

{» «}
4. Sign Out {» «}
{» «}

);
}
}
}
export default SignIn;




## Update AddCat to include user uid

### Pass user to AddCat

Update our main app to pass the user prop.App.js

Now we can use this to include with our data going to firestore. AddCat.js



```typescript
... .add({ ...value, uid: this.props.user.uid, catFactDate: new Date() }) ...

Вход в полноэкранный режим Выйти из полноэкранного режима

Помимо того, нужно ли показывать кнопку Add Cat Fact, мы проверяем, существует ли пользователь. Эта кнопка должна отображаться только в том случае, если пользователь вошел в систему.

 render() { let addCatButton = null; if (this.props.user) addCatButton = ( <button className="myButton" onClick={this.addCatFact}> 2. Add Cat Fact </button> ); return addCatButton; }

Вход в полноэкранный режим Выход из полноэкранного режима

Обновление firestore.rules

«простой текст
сервис cloud.firestore {
match /databases/{database}/documents {
// Заблокировать все
совпадение /{документ=**} {
разрешить чтение: если false; разрешить запись: если false;
}
// Пользователь
совпадение /users/{userId} {
разрешить чтение: если false;
разрешить запись: если request.resource.id == request.auth.uid;
}
// CatFacts
совпадение /catfacts/{catFactId} {
разрешить чтение: если true;
разрешить запись: если request.auth.uid != null && request.resource.data.uid == request.auth.uid;
}
}
}




## Create List of Cat Facts

### Create ListCatFacts

This is probably the most important part of `RxFire` it will return an Observable that you can subscribe to for all changes to a collection by using the function `collectionData` which takes the collection as paramater as well as an option id to create, in our case we pass `catFactsRef.orderBy('catFactDate', 'desc')` and `'catFactId'`.Now we can just use a map to iterate on each catFact, whenever the Observable updates the current `catFact` state the array is updated and we can show the full list update using `{this.state.catfacts.map(catFact => {`.ListCatFacts.js



```typescript
import React from 'react'; import { collectionData, firestore } from '../Firebase'; class ListCatFacts extends React.Compo

Вход в полноэкранный режим Выход из полноэкранного режима

Оцените статью
devanswers.ru
Добавить комментарий