Korišćenje Firebase-a u svrhe autentifikacije

Da bi aplikaciju zadržali jednostavnom, nećemo praviti stranu za registrovanje, već ukoliko se unese email koji ne postoji u bazi, aplikacija će nakon provere i pokušaja da prijavi (uloguje) korisnika taj, nepostojeći mejl, registrovati kao novog korisnika. Tako da, greška će se pojaviti samo u slučaju kada se ukuca pravilna (postojeća) email adresa a pogrešna šifra.

Krećemo tako što ćemo <Button> elementu (unutar LoginForm komponente) dodati prop onPress čija vrednost će biti callback funkcija koja će se izvršavati na svaki klik dugmeta.

<CardSection>
  <Button
    onPress={ }
    text='Log in' />
</CardSection>

Funkciju koju ćemo dodati biće pomoćna funkcija koju ćemo napisati unutar LoginForm komponente (najbolja praksa je da pomoćne komponente pišete iznad render() metode). Potrebno je prvo importovati firebase jer koristimo njegove funkcionalnosti

import firebase from 'firebase';

I zatim pišemo pomoćnu funkciju onButtonClick

onButtonClick() {
    const { email, password } = this.state;

    firebase.auth().signInWithEmailAndPassword(email, password);
}

Koristimo firebase-ovu auth().singInWithEmailAndPassword() metodu i prosledjujemo joj email ipassword iz našeg state objekta. I zatim ćemo ovu pomoćnu funkciju proslediti u onPress <Button> komponente.

<CardSection>
  <Button
    onPress={ this.onButtonClick.bind(this) }
    text='Log in' />
</CardSection>

Pošto je ovo callback funkcija i izvršiće se nekada u budućnosti potrebno je da "bind-ujemo" this funkciji kako bi se this predstavljao samo komponentu. (Da ne bind-ujemo ne bismo mogli koristi state objekat unutar funkcije (const { .. } = this.state ), this bi bio undefined).

Funkcija firebase.auth().signInWithEmailAndPassword(email, password); vraća Promise jer se izvešava asinhrono, jer ovim se pravi request na firebase server, i potrebno neko vreme da stigne odgovor. I kako bi bili svesni toga kada je odgovor stigao koristićemo Promise. Tako da ćemo to iskoristiti kako bi došli do željenog ponašanja naše Log in forme. (Ponašanje objasnjeno u prvom pasusu ovog dela)

Tako da ćemo ovo nadograditi sledećim kodom

firebase.auth().signInWithEmailAndPassword(email, password)
  .catch(() => {
    firebase.auth().createUserWithEmailAndPassword(email, password)
      .catch(() => {
        this.setState({ error: 'Authentication Failed' });
      });
  });

Ovde se dešava sledeće - nakon što je korisnik kliknuo na dugme Log in, aplikacija će prvo pokušati da sa vrednostima email i password uloguje korisnika. Ukoliko dodje do neke greške (to može biti neispravna email adresa ili šifra) tu grešku hvata prvi catch unutar koga će aplikacija pokušati da registriju koristinika (firebase.auth().createUserWithEmailAndPassword(email, password)). Ako se i tada dogodi greška to znači da email već postoji u bazi, a da je šifra pogrešna i zatim se ulazi u poslednji catch pozivamo funckiju setState koja za vrednost svojeg property-ja error postavlja poruku "Authentication Failed". (Da naglasim da je potrebno sada pored email i password inicijalizovati i error unutar state-a.

class LoginForm extends Component {
  state = { email: '', password: '', error: '' };

  ...
}

Generalno ovakvo ponašanje se neće videti u realnim aplikacijama, ali držimo ovu aplikaciju što jednostavnijom.

Da bi korisnik saznao da je došlo do greške, treba je i prikazati. To ćemo učiniti tako što ćemo ubaciti jedan <Text> element iznad dugmeta i prikazati this.state.error unutar njega. I kada nema greške state.error će biti prazan i neće se ništa videti.

 <Text style={ styles.errorStyle }>
   { this.state.error }
 </Text>

Odmah ćemo definisati stil ovog teksta, jer želimo da bude crven i upadljiv.

const styles = {
  errorStyle: {
    fontSize: 20,
    alignSelf: 'center',
    color: 'red'
  }
}

Ukoliko se dogodi greška i state.error dobije odredjenu vrednost koja će se prikazati u ovo <Text> elemntu će ostati tu sve dok se vrednost state.error ne resetuje. Zato je potrebno da pri svakom kliku dugmeta resetujemo tu vrednost, jer ukoliko korisnik prvo pogreši šifru a zatim je pravilno ukuca, error treba da nestane. Zato ćemo samo unutar onButtonClick metode resotovati taj state.

onButtonClick() {
  const { email, password } = this.state;

  this.setState({ error: '' });

  firebase.auth().signInWithEmailAndPassword(email, password)
    .catch(() => {
      firebase.auth().createUserWithEmailAndPassword(email, password)
        .catch(() => {
          this.setState({ error: 'Authentication Failed' });
        });
    });
}

Primetićete da kada probate da se prijavite nakon klika nema nikakvog načina da se korisnik obavesti da je kliknuo dugme i da se bilo šta dešava. O ovome treba da se povede dosta računa pogotovu jer se radi o mobilnoj aplikaciji, i mreža može biti dosta slabija, pogovoru 3G i 4G, gde ovakve vrste poziva mogu dosta dugo da traju. Zato ćemo mi napraviti novu komponentu, koju ćemo nazvati Loader koji će biti mali klasičan spinner i koji će biti na mestu dugmeta dok se ne dobije odgovor od firebase-a prilikom prijave.

Tako da pravimo novi dokument unutar components dadoteke sa imenom Loader.js i pošto je to kompoennta koja će se ili prikazivati (kada se čeka odgovor) ili ne, nemamo potrebe za korišćenjem state pa će biti funkcionalna komponenta :

import React from 'react';
import { View, ActivityIndicator } from 'react-native';

const Loader = ({ size }) => {
  return (
    <View style={ styles.loaderStyle }>
      <ActivityIndicator size={ size || 'large' }/>
    </View>
  );
};

const styles = {
  loaderStyle: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  }
};

export default Loader;

Kako nam je potrebno za Loader nešto što se pokreće/okreće koristićemo React Native komponentu ActivityIndicator, ime vam sve govori. On izgleda ovako

Dodaćemo mu style tako da zauzima celu širinu i bude centriran i horizontalno i vertikalno. ActiviryIndicator može da prima prop size koji odredjuje njegovu veličinu. Kako bi ova komponenta bila reusable Loader će primati ovaj prop od parent-a. Iskoristili smo ovde jedan mali "trik" size={ size || 'large' } koji ustvari znači - Ako je prosledjen prop size od strane parent-a iskoristiga, u suprotnom za vrednost property-ja size iskoristi 'large'. Tako da ukoliko se ne kaže da je Loader mali, uzeće vrednost 'large'.

Sada iskoristimo ovu komponentu unutar LoginForm komponente.
Prvo ga import-ujemo

import Loader from './Loader';

Sledeći korak je da ga iskoristimo. Kada će se Loader prikazivati? U trenutku kada se klikne da dugme "Log in" i biće vidljiv sve dok ne stigne odgovor od firebase-a. Zato ćemo koristi state komponente. Napravićemo još jedan state prop koji će se zvati loading i koji će na početku imati vrednost false. Kada se klikne na dugme dobiće vrednost true i zatim kada se dobije odgovor od firebase-a vratiće se na false. Tako da, zavisno od njegove vrednosti mi ćemo prikazivati Loader komponentu ili ne.

state = { email: '', password: '', error: '', loading: false };

Unutar render metode odradićemo dokonstruckiju state (ovo radimo samo zbog "čistoće" koda)

const { loading } = this.state;

A zatim ćemo u poslednjoj CardSection komponeni umesto Button komponenti napisati sledeće

<CardSection>
    { loading ? <Loader size='small' /> : <Button text='Log in' onPress={ this.onButtonClick.bind(this) } /> }
</CardSection>

Ovo je još jedna ECMA6 feature na koji se treba navići, a ona označava sledeće:
Ako je loading === true prikaži mi <Loader> komponentu a u suprotnom (smešta se iza : ) prikaži mi <Button> komponentu. Treba se navići na ove stvari jer se sve više koriste, a i mnogo su čistije of if i else .

Sada je još potrebno da izmenimo vrednosti state.loading u odredjenim situacijama.
Prvo kada se klikne dugme "Log in" postavićemo da state.loading bude true . (dodajemo unutar onButtonClick metode)

this.setState({ error: '', loading: true });

Tako da će se pored brisanja error poruke na klik dugmeta i loading postaviti na true. Sada je potrebno da nakon vraćenog odgovora od strane firebase-a vratimo loading na false i na taj način sakrijemo Loader komponentu.
Dva slučaja kada ćemo vratiti vrednost na false jeste kada se uspešno izvrši firebase.auth().signInWithEmailAndPassword(email, password) ili
firebase.auth().createUserWithEmailAndPassword(email, password) . Do sada smo koristili samo catch() metodu koja se poziva ukoliko dodje do neke greške, a sada ćemo iskoristi then() metodu koja se izvršava ukoliko nema grešaka i u tom slučaju želimo da vratimo loading na false.

onButtonClick() {
  const { email, password } = this.state;

  this.setState({ error: '', loading: true });

  firebase.auth().signInWithEmailAndPassword(email, password)
    .then( this.setState({ loading: false }) )
    .catch(() => {
      firebase.auth().createUserWithEmailAndPassword(email, password)
        .then( this.setState({ loading: false }) )
        .catch( this.setState({ error: 'Authentication Failed', loading: false }) );
    });
}

Na ovaj način smo pokriti to da bilo kakav odgovor firebase-a bio, mi sakrijemo Loader komponentu. Još jednu stvar koju ćemo želeti da imao jeste to da se nakon uspešne prijave obrišu vrednosti iz naših input komponenti a to ćemo uraditi jednostavnim čišćenjem state-a. Tako da ćemo napisati jednu novu pomoćnu metodu

onLoginSuccess() {
  this.setState({
    email: '',
    password: '',
    error: '',
    loading: false,
  });
}

I iskoristi je unutar onButtonClick() metode, radi čistog koda napisaćemo i pomoćnu funkciju u slučaju greške

onLoginFail() {
  this.setState({ error: 'Authentication Failed', loading: false });
}

Iskoristimo ih unutar onButtonClick() metode

onButtonClick() {
  const { email, password } = this.state;

  this.setState({ error: '', loading: true });

  firebase.auth().signInWithEmailAndPassword(email, password)
    .then( this.onLoginSuccess.bind(this) )
    .catch(() => {
      firebase.auth().createUserWithEmailAndPassword(email, password)
        .then( this.onLoginSuccess.bind(this) )
        .catch( this.onLoginFail.bind(this) );
    });
}

Sada je kod i čistiji i čitljiviji.

LoginForm.js izlgeda sada ovako:

import React, { Component } from 'react';
import { Text } from 'react-native';
import firebase from 'firebase';
import Button from './Button';
import Card from './Card';
import CardSection from './CardSection';
import Input from './Input';
import Loader from './Loader';


class LoginForm extends Component {
  state = { email: '', password: '', error: '', loading: false };

  onButtonClick() {
    const { email, password } = this.state;

    this.setState({ error: '', loading: true });

    firebase.auth().signInWithEmailAndPassword(email, password)
      .then( this.onLoginSuccess.bind(this) )
      .catch(() => {
        firebase.auth().createUserWithEmailAndPassword(email, password)
          .then( this.onLoginSuccess.bind(this) )
          .catch( this.onLoginFail.bind(this) );
      });
  }

  onLoginFail() {
     this.setState({ error: 'Authentication Failed', loading: false });
  }

  onLoginSuccess() {
    this.setState({
      email: '',
      password: '',
      error: '',
      loading: false,
    });
  }

  render() {
    const { loading } = this.state;

    return (
      <Card>
        <CardSection>
          <Input
            placeholder= '[email protected]'
            label= 'Email'
            value={ this.state.email }
            onChangeText={ email => this.setState({ email: email }) } />
        </CardSection>
        <CardSection>
          <Input
            secureTextEntry={ true }
            placeholder= 'password'
            label= 'Password'
            value={ this.state.password }
            onChangeText={ password => this.setState({ password: password }) } />
        </CardSection>

        <Text style={ styles.errorStyle }>
          { this.state.error }
        </Text>

        <CardSection>
          { loading ? <Loader size='small' /> : <Button text='Log in' onPress={ this.onButtonClick.bind(this) } /> }
        </CardSection>
      </Card>
    );
  }
}

const styles = {
  errorStyle: {
    fontSize: 20,
    alignSelf: 'center',
    color: 'red'
  }
}

export default LoginForm;

Sada možete da reload-ujete svoj simualtor i testirate ponašanje forme. Kada popunite formu i kliknete na dugme dugme će nestati i pojaviće se Loader komponenta. U slučaju da pogrešite šifru pojaviće se greška i vratiti dugme. Ukoliko zatim unesete tačne podatke i kliknete na dugme Loader će se ponovo pojaviti i ukoliko prijava bude u redu, dugme se vraća na mesto i forma će se isprazniti.
Medjutim, to nije dovoljno za korisnike da znaju da su se uspešno prijavili tako da će naš sledeći korak biti upravo to. Nakon uspešne prijave prikazati korisniku nešo drugo a formu sakriti.

results matching ""

    No results matching ""