Terakhir diperbarui: 2021-05-03

Penulis: Habibie Ed Dien, S.Kom., M.T.

Redux-Thunk dan Firebase Login di ReactJS

Pada codelab ini Anda akan mempelajari tentang penggunaan Redux-Thunk dan penerapannya dengan Firebase Login di ReactJS.

Pengetahun yang Anda harus miliki

Sebelum memulai codelab ini, sebaiknya Anda memiliki pengetahuan dasar tentang:

Apa yang Anda akan pelajari

Apa yang Anda perlu persiapkan

Apa itu "Thunk" ?

Istilah "thunk" merupakan humoris atau candaan kata past tense dari "think". Sebenarnya Redux Thunk adalah middleware yang memungkinkan Anda memanggil pembuat aksi yang mengembalikan fungsi sebagai ganti objek aksi. Fungsi itu menerima metode pengiriman penyimpanan, yang kemudian digunakan untuk mengirim aksi sinkron di dalam isi fungsi setelah operasi asinkron selesai.

Kita mengetahui bahwa Redux mengembalikan dalam bentuk props actions yang didefinisikan oleh Reducers, namun masalahnya jika kita ingin mengembalikan sebuah function, Redux tidak dapat menanganinya. Oleh karena itu, kita membutuhkan middleware yang berfungsi untuk mengembalikan action. Jadi, Redux Thunk adalah sebuah middleware yang memungkinkan untuk menulis Action yang mengembalikan function, bukan action.

Sebuah "thunk" adalah sebuah fungsi yang membungkus suatu ekspresi untuk dilakukan evaluasi nanti. Perhatikan contoh kode JavaScript berikut ini.

// calculation of 1 + 2 is immediate
// x === 3
let x = 1 + 2;
 
// calculation of 1 + 2 is delayed
// foo can be called later to perform the calculation
// foo is a thunk!
let foo = () => 1 + 2;

Cara Menggunakan Redux Thunk

Untuk menggunakan library ini, perlu kita install terlebih dahulu di project React kita. Caranya lakukan perintah berikut pada terminal atau console.

npm install --save redux-thunk

Kemudian untuk mengaktifkan Redux Thunk, gunakan fungsi applyMiddleware():

import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers/index';
 
// Note: this API requires redux@>=3.1.0
const store = createStore(
  rootReducer,
  applyMiddleware(thunk)
);

Pada praktikum ini, kita dapat melanjutkan dari codelab sebelumnya (Codelab 10 - Firebase di ReactJS). Praktikum ini akan membuat form login yang dapat mengarahkan ke halaman yang dilindungi dengan password. Jadi, halaman tersebut tidak akan bisa diakses kecuali user telah melakukan login. Untuk membuatnya, silakan lakukan langkah-langkah praktikum berikut ini.

  1. Anda dapat membuat project baru React atau melanjutkan praktikum dari Codelab sebelumnya.
  2. Ubahlah isi kode firebase.config.js seperti berikut.
import firebase from "firebase/app";
import "firebase/auth";

export const firebaseConfig = {
  apiKey: "isikan sesuai value dari console firebase Anda",
  authDomain: "isikan sesuai value dari console firebase Anda",
  projectId: "isikan sesuai value dari console firebase Anda",
  storageBucket: "isikan sesuai value dari console firebase Anda",
  messagingSenderId: "isikan sesuai value dari console firebase Anda",
  appId: "isikan sesuai value dari console firebase Anda",
  measurementId: "isikan sesuai value dari console firebase Anda"
};

export const myFirebase = firebase.initializeApp(firebaseConfig);
  1. Buatlah folder dan file baru di src/redux/actions/auth.js lalu isi dengan kode berikut.
import { myFirebase } from "../../firebase.config";

export const LOGIN_REQUEST = "LOGIN_REQUEST";
export const LOGIN_SUCCESS = "LOGIN_SUCCESS";
export const LOGIN_FAILURE = "LOGIN_FAILURE";

export const LOGOUT_REQUEST = "LOGOUT_REQUEST";
export const LOGOUT_SUCCESS = "LOGOUT_SUCCESS";
export const LOGOUT_FAILURE = "LOGOUT_FAILURE";

export const VERIFY_REQUEST = "VERIFY_REQUEST";
export const VERIFY_SUCCESS = "VERIFY_SUCCESS";
  1. Kemudian tambahkan fungsi action dibawahnya.
const requestLogin = () => {
  return {
    type: LOGIN_REQUEST
  };
};

const receiveLogin = user => {
  return {
    type: LOGIN_SUCCESS,
    user
  };
};

const loginError = () => {
  return {
    type: LOGIN_FAILURE
  };
};

const requestLogout = () => {
  return {
    type: LOGOUT_REQUEST
  };
};

const receiveLogout = () => {
  return {
    type: LOGOUT_SUCCESS
  };
};

const logoutError = () => {
  return {
    type: LOGOUT_FAILURE
  };
};

const verifyRequest = () => {
  return {
    type: VERIFY_REQUEST
  };
};

const verifySuccess = () => {
  return {
    type: VERIFY_SUCCESS
  };
};
  1. Lalu tambahkan juga dibawahnya fungsi untuk login, logout, dan verifikasi akun dengan firebase seperti berikut.
export const loginUser = (email, password) => dispatch => {
  dispatch(requestLogin());
  myFirebase
    .auth()
    .signInWithEmailAndPassword(email, password)
    .then(user => {
      dispatch(receiveLogin(user));
    })
    .catch(error => {
      //Do something with the error if you want!
      dispatch(loginError());
    });
}

export const logoutUser = () => dispatch => {
  dispatch(requestLogout());
  myFirebase
    .auth()
    .signOut()
    .then(() => {
      dispatch(receiveLogout());
    })
    .catch(error => {
      //Do something with the error if you want!
      dispatch(logoutError());
    });
};

export const verifyAuth = () => dispatch => {
  dispatch(verifyRequest());
  myFirebase.auth().onAuthStateChanged(user => {
    if (user !== null) {
      dispatch(receiveLogin(user));
    }
    dispatch(verifySuccess());
  });
};
  1. Selanjutnya buat folder dan file baru di src/redux/reducers/auth.js.
import {
  LOGIN_REQUEST,
  LOGIN_SUCCESS,
  LOGIN_FAILURE,
  LOGOUT_REQUEST,
  LOGOUT_SUCCESS,
  LOGOUT_FAILURE,
  VERIFY_REQUEST,
  VERIFY_SUCCESS
} from "../actions/auth";

export default (
  state = {
    isLoggingIn: false,
    isLoggingOut: false,
    isVerifying: false,
    loginError: false,
    logoutError: false,
    isAuthenticated: false,
    user: {}
  },
  action
) => {
  switch (action.type) {
    case LOGIN_REQUEST:
      return {
        ...state,
        isLoggingIn: true,
        loginError: false
      };
    case LOGIN_SUCCESS:
      return {
        ...state,
        isLoggingIn: false,
        isAuthenticated: true,
        user: action.user
      };
    case LOGIN_FAILURE:
      return {
        ...state,
        isLoggingIn: false,
        isAuthenticated: false,
        loginError: true
      };
    case LOGOUT_REQUEST:
      return {
        ...state,
        isLoggingOut: true,
        logoutError: false
      };
    case LOGOUT_SUCCESS:
      return {
        ...state,
        isLoggingOut: false,
        isAuthenticated: false,
        user: {}
      };
    case LOGOUT_FAILURE:
      return {
        ...state,
        isLoggingOut: false,
        logoutError: true
      };
    case VERIFY_REQUEST:
      return {
        ...state,
        isVerifying: true,
        verifyingError: false
      };
    case VERIFY_SUCCESS:
      return {
        ...state,
        isVerifying: false
      };
    default:
      return state;
  }
};
  1. Buat file baru index.js di folder reducers tersebut yang isinya seperti berikut.
import { combineReducers } from "redux";
import auth from "./auth";

export default combineReducers({ auth });
  1. Buat file baru di src/redux/configureStore.js untuk mengonfigurasi store kita dengan Redux Thunk. Jangan lupa library Redux Thunk diinstall seperti pada langkah materi sebelumnya pada codelab ini. Isi kode dari file configureStore.js adalah sebagai berikut.
import { applyMiddleware, createStore } from "redux";
import thunkMiddleware from "redux-thunk";

import { verifyAuth } from "./actions/auth";
import rootReducer from "./reducers";

export default function configureStore (persistedState) {
  const store = createStore(
    rootReducer,
    persistedState,
    applyMiddleware(thunkMiddleware)
  );
  store.dispatch(verifyAuth());
  return store;
}

Membuat Form Login dan Halaman Home

  1. Kita buat komponen form Login atau dapat menggunakan komponen Login dari Codelab 10. Buka file Login.js di src/components. Sesuaikan Isi kodenya seperti berikut.
import { useState } from "react";
import { connect } from "react-redux";
import { Redirect } from "react-router-dom";
import { loginUser } from "../redux/actions/auth";

function Login (props) {
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");

  const handleForm = e => {
    e.preventDefault();

    const { dispatch } = props;
    dispatch(loginUser(email, password));
  };

  const { isLoggingIn, loginError, isAuthenticated } = props;

  if (isAuthenticated) {
    return <Redirect to="/" />
  } else {
    return (
      <div>
        <h1>Login</h1>
        <form onSubmit={e => handleForm(e)}>
          <input
            value={email}
            onChange={e => setEmail(e.target.value)}
            name="email"
            type="email"
            placeholder="email"
            required
          />
          <input
            value={password}
            onChange={e => setPassword(e.target.value)}
            name="password"
            type="password"
            placeholder="password"
            required
          />
          <hr />
          <button type="submit">Login</button>
          <hr />
          <span>{loginError && ("Email atau Password Salah!")}</span>
          <span>{isLoggingIn && ("Sedang login ...")}</span>
        </form>
      </div>
    );
  }
  
}

function mapStateToProps(state){
  return {
    isLoggingIn: state.auth.isLoggingIn,
    loginError: state.auth.loginError,
    isAuthenticated: state.auth.isAuthenticated
  }
}

export default connect(mapStateToProps)(Login);
  1. Kemudian buat file Home.js di folder src/components.
import React from "react";
import { connect } from "react-redux";
import { logoutUser } from "../redux/actions/auth";

class Home extends React.Component {

  handleLogout = () => {
    const { dispatch } = this.props;
    dispatch(logoutUser());
  };

  render () {
    const { isLoggingOut, logoutError } = this.props;
    return (
      <div>
        <h1>Halaman ini dilindungi dengan password.</h1>
        <p>Semua routes di sini akan dilindungi.</p>
        <button onClick={this.handleLogout}>Keluar</button>
        {isLoggingOut && <p>Sedang proses logout...</p>}
        {logoutError && <p>Terjadi galat saat logout!</p>}
      </div>
    );
  }

}

function mapStateToProps (state) {
  return {
    isLoggingOut: state.auth.isLoggingOut,
    logoutError: state.auth.logoutError
  };
}

export default connect(mapStateToProps)(Home);

Membuat Route yang Diproteksi

  1. Buatlah folder dan file baru di src/routes/protectedRoute.js yang isinya sebagai berikut.
import React from "react";
import { Route, Redirect } from "react-router-dom";

const ProtectedRoute = ({
  component: Component,
  isAuthenticated,
  isVerifying,
  ...rest
}) => (
  <Route
    {...rest}
    render={props =>
      isVerifying ? (
        <div />
      ) : isAuthenticated ? (
        <Component {...props} />
      ) : (
        <Redirect
          to={{
            pathname: "/login",
            state: { from: props.location }
          }}
        />
      )
    }
  />
);

export default ProtectedRoute;

Membuat App Utama

  1. Kita buat komponen Root yang berfungsi untuk memverifikasi user yang sudah terautentikasi atau belum, sehingga bisa dilakukan kontrol terhadap route ke App kita. Buatlah file baru di src/Root.js dengan isi kode sebagai berikut.
import { Route, Switch } from "react-router-dom";
import { connect } from "react-redux";
import ProtectedRoute from "./routes/protectedRoute";
import Home from "./components/Home";
import Login from "./components/Login";

function Root (props) {
  const { isAuthenticated, isVerifying } = props;
  return (
    <Switch>
      <ProtectedRoute
        exact
        path="/"
        component={Home}
        isAuthenticated={isAuthenticated}
        isVerifying={isVerifying}
      />
      <Route path="/login" component={Login} />
    </Switch>
  );
}

function mapStateToProps (state) {
  return {
    isAuthenticated: state.auth.isAuthenticated,
    isVerifying: state.auth.isVerifying
  };
}

export default connect(mapStateToProps)(Root);
  1. Kita pindah ke file src/App.js yang menentukan tampilan render pada aplikasi React kita.

  1. Jangan lupa import yang diperlukan pada file src/App.js
import React from "react";
import { Provider } from "react-redux";
import { BrowserRouter as Router } from "react-router-dom";

import configureStore from "./redux/configureStore";
import Root from "./Root";
import './App.css';
  1. Simpan semua pekerjaan lalu lihat hasilnya di browser. Seharusnya form login sudah tampil seperti pada gambar berikut.

Kemudian coba lakukan login dengan email dan password yang telah didaftarkan sebelumnya di Codelab 10. Jika login sukses, maka akan mengarah ke komponen Home seperti pada gambar berikut.

  1. Berdasarkan praktikum yang telah Anda lakukan, jelaskan perbedaan fitur yang ada dalam komponen Login dan Home! Mengapa komponen Login tidak menggunakan class seperti pada komponen Home ?
  2. Jelaskan kegunaan dan alur logika dari protectedRoute.js !
  3. Coba lakukan login dengan email atau password yang salah, apa yang terjadi? Jelaskan!
  4. Jika Anda berada di halaman Home, coba akses form login tanpa melakukan logout. Apakah form login bisa diakses? Jelaskan!
  5. Tambahkan menu Register pada form login sehingga user yang belum terdaftar dapat melakukan registrasi! Jika registrasi sukses, maka user langsung diarahkan ke halaman Home.

Selamat, Anda telah berhasil menyelesaikan codelab ini. Semoga mendapatkan ilmu yang bermanfaat.

Apa selanjutnya?

Silakan cek beberapa sumber belajar lainnya...

Referensi