React Native: Authentication với Firebase đơn giản và dễ hiểu

0
45
Bài này thuộc phần 5 của 6 phần trong series React Native Training cho người mới

react-native-authentication-compressed

Firebase là một kiểu Back-end service của google, với rất nhiều tính năng mạnh mẽ giúp cho việc phát triển ứng dụng mobile trở nên nhanh chóng hơn rất nhiều. Các tính năng được cung cấp bởi Firebase thì rất nhiều: Realtime Database, Authentication, Cloud Firestore, Cloud Functions, Crashlytics…

Hôm nay, mình sẽ hướng dẫn các bạn cách xây dựng tính năng xác thực(authentication) bằng email và password sử dụng Firebase cho ứng dụng React Native..

Ở bài viết này, chúng ta sẽ sử dụng Firebase SDK và thư viện react-native-firebase. Đây là thư viện rất nổi tiếng để kết nối ứng dụng React Native với Firebase.

Yêu cầu thư viện:

  • FirebaseSDK
  • Thư viện react-native-firebase
  • Thư viện react-navigation

Cài đặt project

Để bắt đầu, chúng ta có thể sử dụng project mẫu được cung cấp sẵn bởi thư viện react-native-firebase. Các bạn download trên github về là dùng được luôn

(Nếu bạn muốn tích hợp tính năng authentication này vào dự án đã có sẵn thì bạn có thể tham khảo hướng dẫn tại đây)

Sau khi bạn download source code ở trên thì làm theo hướng dẫn trên trang github để cấu hình cho dự án có thể build và chạy được nhé. Nếu bạn gặp khó khăn khi đọc hướng dẫn trên github thì có thể tham khảo bài viết mà mình đã hướng dẫn để có thể build ứng dụng React Native.

Sau khi cấu hình dự án thành công thì chúng ta sẽ tiếp tục thực hiện code phần chính cho bài viết này ở phần bên dưới)

Bật tính năng Email & Password Authentication trên Firebase

Mình minh họa sơ đồ luồng logic của tính năng như hình bên dưới

React Native: Authentication

Để có thể sử dụng email và password cho tính năng login, thì chúng ta phải bật tính năng này trong firebase console.

Các bạn làm như sau:

Vào Firebase Project mà đã tạo → Authentication → Sign-in Method

Click vào mục Email/Password chọn enable. Đại khái như hình bên dưới:

React Native: Authentication

Xây dựng giao diện các màn hình bằng React Native

Như hình minh họa về luồng logic mà mình đã trình bày ở trên, thì ứng dụng của chúng ta sẽ cần phải có tối thiểu 4 màn hình: Loading, SignUp, Login, and Main

  • Màn hình Loading: Màn hình sẽ hiển thị khi chúng ta kết nối ứng dụng tới firebase để tiến hành authenticate (Kiểu ProgressBar để người dùng biết là ứng dụng vẫn đang hoạt động mà không phải bị treo)
  • Màn hình SignUp: Màn hình để User có thể đăng ký tài khoản bằng email và password
  • Màn hình Login: Tất nhiên là cho người đã đăng kí có thể login
  • Màn hình Main: Là màn hình sau khi người đã login thành công

Để có thể transit giữa các màn hình, chúng ta sẽ sử dụng thư viện react-navigation. Cài đặt thư viện này bằng lệnh

yarn add react-navigation

Dưới đây là code cho từng màn hình mà mình đã nói ở trên
Loading.js

// Loading.js
import React from 'react'
import { View, Text, ActivityIndicator, StyleSheet } from 'react-native'
export default class Loading extends React.Component {
  render() {
    return (
      <View style={styles.container}>
        <Text>Loading</Text>
        <ActivityIndicator size="large" />
      </View>
    )
  }
}
const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  }
})

SignUp.js

// SignUp.js
import React from 'react'
import { StyleSheet, Text, TextInput, View, Button } from 'react-native'
export default class SignUp extends React.Component {
  state = { email: '', password: '', errorMessage: null }
handleSignUp = () => {
  // TODO: Firebase stuff...
  console.log('handleSignUp')
}
render() {
    return (
      <View style={styles.container}>
        <Text>Sign Up</Text>
        {this.state.errorMessage &&
          <Text style={{ color: 'red' }}>
            {this.state.errorMessage}
          </Text>}
        <TextInput
          placeholder="Email"
          autoCapitalize="none"
          style={styles.textInput}
          onChangeText={email => this.setState({ email })}
          value={this.state.email}
        />
        <TextInput
          secureTextEntry
          placeholder="Password"
          autoCapitalize="none"
          style={styles.textInput}
          onChangeText={password => this.setState({ password })}
          value={this.state.password}
        />
        <Button title="Sign Up" onPress={this.handleSignUp} />
        <Button
          title="Already have an account? Login"
          onPress={() => this.props.navigation.navigate('Login')}
        />
      </View>
    )
  }
}
const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center'
  },
  textInput: {
    height: 40,
    width: '90%',
    borderColor: 'gray',
    borderWidth: 1,
    marginTop: 8
  }
})

Login.js

// Login.js
import React from 'react'
import { StyleSheet, Text, TextInput, View, Button } from 'react-native'
export default class Login extends React.Component {
  state = { email: '', password: '', errorMessage: null }
  handleLogin = () => {
    // TODO: Firebase stuff...
    console.log('handleLogin')
  }
  render() {
    return (
      <View style={styles.container}>
        <Text>Login</Text>
        {this.state.errorMessage &&
          <Text style={{ color: 'red' }}>
            {this.state.errorMessage}
          </Text>}
        <TextInput
          style={styles.textInput}
          autoCapitalize="none"
          placeholder="Email"
          onChangeText={email => this.setState({ email })}
          value={this.state.email}
        />
        <TextInput
          secureTextEntry
          style={styles.textInput}
          autoCapitalize="none"
          placeholder="Password"
          onChangeText={password => this.setState({ password })}
          value={this.state.password}
        />
        <Button title="Login" onPress={this.handleLogin} />
        <Button
          title="Don't have an account? Sign Up"
          onPress={() => this.props.navigation.navigate('SignUp')}
        />
      </View>
    )
  }
}
const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center'
  },
  textInput: {
    height: 40,
    width: '90%',
    borderColor: 'gray',
    borderWidth: 1,
    marginTop: 8
  }
})

Main.js

// Main.js
import React from 'react'
import { StyleSheet, Platform, Image, Text, View } from 'react-native'
export default class Main extends React.Component {
  state = { currentUser: null }
render() {
    const { currentUser } = this.state
return (
      <View style={styles.container}>
        <Text>
          Hi {currentUser && currentUser.email}!
        </Text>
      </View>
    )
  }
}
const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center'
  }
})

Sau khi đã hoàn thành tất cả các màn hình thì chúng ta sẽ kết nối chúng vào file App.js để có thể navigate

App.js

import React from 'react'
import { StyleSheet, Platform, Image, Text, View } from 'react-native'
import { SwitchNavigator } from 'react-navigation'
// import the different screens
import Loading from './Loading'
import SignUp from './SignUp'
import Login from './Login'
import Main from './Main'
// create our app's navigation stack
const App = SwitchNavigator(
  {
    Loading,
    SignUp,
    Login,
    Main
  },
  {
    initialRouteName: 'Loading'
  }
)
export default App

Ok, bạn thử chạy ứng dụng xem thế nào? Bạn sẽ thấy màn hình Loading với thanh ActivityIndicator quay quay mãi mãi. Hiện tại thì ứng dụng của mình mới chỉ đến được đây thôi.

Chúng ta sẽ tiến hành code phần authenticate ngay bên dưới để khi User nhập email và password thành công thì đưa User sang màn hình Main

Xác định một User là authenticated hay chưa?

Chúng ta có thể sử dụng Firebase để xác định trạng thái xác thực của user. Chỉ đơn giản là thêm một hàm kiểm tra trạng thái xem user đã login rồi hay chưa ở màn hình Loading

// Loading.js
// Omitted other imports...
import firebase from 'react-native-firebase'
export default class Loading extends React.Component {
  componentDidMount() {
    firebase.auth().onAuthStateChanged(user => {
      this.props.navigation.navigate(user ? 'Main' : 'SignUp')
    })
  }
// Omitted the rest of the file...

Ở đoạn code trên, chúng ta sử dụng onAuthStateChanged listener để lấy trạng thái xác thực hiện tại của user. Nếu user này đã xác thực (authenticated) thì đưa user này vào màn hình Main. Còn không, thì user này sẽ được chuyển sang màn hình SignUp để tiến hành đăng kí tài khoản mới

Giả sử, user chưa loggin thì user sẽ thấy màn hình Loading trong thời gian ngắn, sau đó thì sẽ chuyển sang màn hình SignUp

Đăng kí tài khoản mới

Để có thể đăng kí tài khoản mới, chúng ta sử dụng hàm handleSignUp

// SignUp.js
// Omitted other imports...
import firebase from 'react-native-firebase'
export default class SignUp extends React.Component {
  state = { email: '', password: '', errorMessage: null }
  handleSignUp = () => {
    firebase
      .auth()
      .createUserWithEmailAndPassword(this.state.email, this.state.password)
      .then(() => this.props.navigation.navigate('Main'))
      .catch(error => this.setState({ errorMessage: error.message }))
  }
// Omitted the rest of the file...

Khi user submit thông tin và thành công, chúng ta sẽ đưa người dùng sang màn hình Main. Còn nếu bị lỗi(Ví dụ: Lỗi đường truyển, lỗi dữ liệu…) thì hiển thị thông báo ra màn hình

Hiển thị thông tin đăng nhập ở màn hình Main

Với những implementation hiện tại, thì chúng ta chỉ thấy màn hình Main trắng bóc sau khi user đã logined. Để đỡ nhàm chán thì chúng ta sẽ hiên thị thông tin của họ (Ví dụ là Email), và thông tin này sẽ được lấy từ firebase.

// Main.js
// Omitted other imports...
import firebase from 'react-native-firebase'
export default class Main extends React.Component {
  state = { currentUser: null }
  componentDidMount() {
    const { currentUser } = firebase.auth()
    this.setState({ currentUser })
}
// Omitted the rest of the file...

Bây giờ thì ở màn hình Main đã hiển thị Email của user. Nếu bạn refresh lại app, thì app nên tự động chuyển đến màn hình Main vì user đã logined trước đó rồi.
Bước cuối cùng của bài hướng dẫn này là tính năng login cho người đã đăng kí

Login từ user đã tồn tại

Với màn hình login này thì cũng đơn giản thôi,  chúng ta sử dụng hàm handleLogin để tiến hành xác thực

// Login.js
// Omitted other imports...
import firebase from 'react-native-firebase'
export default class Login extends React.Component {
  state = { email: '', password: '', errorMessage: null }
  handleLogin = () => {
    const { email, pasword } = this.state
    firebase
      .auth()
      .signInWithEmailAndPassword(email, password)
      .then(() => this.props.navigation.navigate('Main'))
      .catch(error => this.setState({ errorMessage: error.message }))
  }
// Omitted the rest of the file...

Sweet!
Đã xong! Như vậy là chúng ta đã hoàn thành đầy đủ các tính năng cần thiết cho một user: SignUp, Login, lấy thông tin Email. Bạn có thể download full source code của bài viết tại đây

Ứng dụng của bạn có sử dụng đến tính năng login này không? Mình rất muốn biết ý kiến của bạn về bài viết này, hãy để lại comment nhé. Bài viết sau mình sẽ trình bày chi tiết về navigator trong React Native, các bạn đón đọc nhé

Xem tiếp các bài trong Series
Phần trước: React Native – Phân biệt Props và State cực kỳ đơn giản và dễ hiểuPhần kế tiếp: 6 cách đơn giản để tăng tốc độ ứng dụng React Native

BÌNH LUẬN

Please enter your comment!
Please enter your name here