ReactJs: HOC sederhana untuk User Roles — Bagian 1

HOC — merupakan singkatan dari High Order Component. Sebuah konsep atau teknik dengan logika didalamnya yang dapat digunakan kembali.

A higher-order component (HOC) is an advanced technique in React for reusing component logic. HOCs are not part of the React API, per se. They are a pattern that emerges from React’s compositional nature.” — ReactJs official Docs

Dalam implementasi sebuah logika pada komponen di reactjs yang kadang kala kita membuatnya berulang-ulang padahal kita dapat membuatnya cuma 1 kali. Nah, untuk membuat komponen seperti itu hal yang dibutuhkan yakni memisahkan sebuah komponen antara View/Tampilan dan logika tertentu.

Pada tulisan kali ini, saya akan memberikan sebuah contoh bagaimana membuat sebuah HOC dengan Kasus / Case Study yakni Roles.

Umumnya roles digunakan untuk dapat membedakan hak akses dari setiap user/pengguna yang masuk kedalam applikasi web.

Sederhananya , Admin dapat mengakses semua halaman dan user biasa hanya dapat mengakses beberapa halaman saja.

Kebutuhan

  • create-react-app (untuk memudahkan development)
  • react-router

untuk memulai cukup sederhana saja , kita menggunakan Create-React-app dan React-Router.

Langkah-Langkah

  • Buat Project ReactJS seperti biasa dengan perintan create-react-app namaProyek
  • pasang react-router dengan menggunakan package-manager seperti yarn atau npm
$ create-react-app react-roles
$ cd react-roles
$ yarn add react-router-dom --dev # jika menggunakan yarn
$ npm install react-router-dom --save-dev # jika menggunakan npm
$ # pada artikel ini menggunakan React-Router 4.3.1 dan ReactJs 16.4.2
  • Tambahkan 2 File (Router.js & Auth.js) pada folder src/

Pertama kita menulis kode pada Router.js, Berikut contoh kodenya:

import React, {Fragment} from 'react';
import {
BrowserRouter as Router,
Route,
Link
} from "react-router-dom";

const Navigation = () => (
<ul style={{display: "flex", justifyContent: "space-around" }}>
<li><Link to="/"> Home </Link></li>
<li><Link to="/admin"> Admin Page </Link></li>
<li><Link to="/user"> User Page </Link></li>
</ul>
);

const HomeComponent = () => (
<h1>Selamat datang</h1>
);

const AdminComponent = () => (
<h1>Hai Admin, Kamu cantik deh!</h1>
);

const UserComponent = () => (
<h1>Hai user, Jomblo kan!</h1>
);

const MainRouter = () => (
<Router>
<Fragment>
<Navigation/>
<Route path="/" component={HomeComponent} exact={true} />
<Route path="/admin" component={AdminComponent} />
<Route path="/user" component={UserComponent} />
</Fragment>
</Router>
);

export default MainRouter;

pada kode diatas saya mendeklarasikan 4 functional component yakni Navigation, HomeComponent, AdminComponent, & UserComponent.

  • MainRouter merupakan component yang mengatur alamat atau router dari React-App, yang didalamnya didefinisikan alamat-alamat atau rute pada applikasi ini.
  • Navigation untuk membuat komponen berupa Daftar menu.
  • HomeComponent untuk menampilkan isi dari alamat atau halaman utama.
  • AdminComponent untuk menampilkan halaman yang nantinyahanya dapat diakses oleh Admin.
  • UserComponent untuk menampilkan halaman yang nantinya dapat di akses oleh User.

dan pada baris akhir kode diatas, ada Export Default yang digunakan untuk mengexport komponen atau function javascript yang dimana menjadi default ketika di import nantinya.

Kemudian, ubah beberapa bagian atau baris kode pada App.js , berikut kodenya:

import React, { Component } from 'react';
//import logo from './logo.svg';
//import './App.css';
import Routers from './Router';

class App extends Component {
render() {
return <Routers/>;
}
}

export default App;

jika selesai, silahkan di jalankan proyek ReactJs tersebut, maka bentuk dari applikasi tersebut seperti gambar gerak dibawah ini.

Okay, sekian untuk tahap ini hehehe. maksudnya untuk tahap bagaimana menggunakan React-Router v4.

Sekarang, kita lanjut bagaimana membuat Auth, Membuat HOC, dan Meng-implementasikan Roles pada Komponen di ReactJs.

Tulis kode berikut pada file src/Auth.js yang sudah di buat sebelumnya (asumsi file Auth.js saat ini kosong)

import React, {Component} from 'react';
/**
* Get User from Local Storage
* https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API
* @return {obeject} UserData
* {
* username: string,
* role: string
* }
*/
const GetUser = () => {
const fromStorage = JSON.parse(localStorage.getItem("user"));
return !!fromStorage ? fromStorage : {user: '', role: 'guest'};
}

/**
* Checking role its valid
* @param {object}
* {
* role: string,
* allowedRoles: array,
* }
* @return {boolean}
*/
const isValidRole = ({role, allowedRoles}) => allowedRoles.includes(role);

/**
* Authorization (High Order Component Concept)
* @param {array} allowedRoles
* @param {object} WrappedComponent
* @return {object} React.Component
*
* Example:
* # set AllowedRoles with Component
* const AuthComponent = Authorization(['user','admin','superman'])(MyComponent)
*
* # set AllowedRoles without Component
* const AuthHOC = Authorization(['user','admin','superman'])
* const MyComponent = () => <h1> Hello </h1>
* const AuthComponent = AuthHOC(MyComponent);
*
* ReactDOM.render( <AuthComponent/>, target);
*/
const Authorization = allowedRoles => wrappedComponent => class withAuth extends Component {
constructor(props){
super(props);
this.state = {
user: GetUser(), // state user assign value from GetUser function
}
}

render(){
const {role} = this.state.user;
return isValidRole({role: role, allowedRoles: allowedRoles}) ?
<wrappedComponent/>:
<h1> Hai! kamu tidak boleh masuk dihalaman ini, rasakan chidorii ini - regards {allowedRoles.join(', ')}</h1>
}
}


/**
* define administrator role
* use: Admin(<Component/>)
*/
export const Admin = Authorization(['admin']);

/**
* define user role
* use: User(<Component/>)
*/
export const User = Authorization(['admin','user']);

export default {
Admin,
User,
}

pada kode diatas saya mendeklarasikan / mendefinisikan 5 Function, yakni

  • GetUser : sebagai fungsi yang mengembalikan nilai berupa data dari user (Store/Storage). pada fungsi ini, saya menggunakan Web Storage (Local Storage) pada browser, bagian ini bisa digantikan dengan storage/store lainnya, seperti Redux, Context, dan sejenisnya. Yang paling penting kita memahami bentuk/structure dari data tersebut.
  • isValidRole : fungsi yang melakukan pengecheckan atau memeriksa jenis role dari user.
  • Authorization : HOC — High order component, yakni sebuah fungsi (functional) yang mengembalikan atau membuat sebuah class / react.component baru, yang dimana komponen atau class yang ingin di kembalikan akan melalui proses / logika tertentu sebelum class itu di render. wrappedComponent merupakan component atau class yang akan di kembalikan tergantung kondisi di dalam function HOC tersebut.
  • Admin & User : merupakan function yang meng-implementasikan dari function Authorization . Masing-masing telah diatur allowedRoles dalam dengan jenis nilai Array dan nantinya akan Meng-implementasi WrappedComponent ketika fungsi ini dijalankan dan diberikan parameter berupa komponent/element yang dibungkus oleh HOC Authorization.

setelah menambahkan kode diatas pada Auth.js, lanjut dengan menambahkan kode pada Routers.js yang dimana kita implementasikan Logika yang kita tulis pada Auth.js didalam Routers.js

Berikut contoh kodenya.

import {Admin, Auth} from './Auth';
...
bagian router
...
<Route path="/admin" component={Admin(AdminComponent)} />               <Route path="/user" component={User(UserComponent)} />
import React, {Fragment} from 'react';
import {
BrowserRouter as Router,
Route,
Link
} from "react-router-dom";

import {Admin, User} from './Auth'; // Add this line

const Navigation = () => (
<ul style={{display: "flex", justifyContent: "space-around" }}>
<li><Link to="/"> Home </Link></li>
<li><Link to="/admin"> Admin Page </Link></li>
<li><Link to="/user"> User Page </Link></li>
</ul>
);

const HomeComponent = () => (
<h1>Selamat datang</h1>
);

const AdminComponent = () => (
<h1>Hai Admin, Kamu cantik deh!</h1>
);

const UserComponent = () => (
<h1>Hai user, Jomblo kan!</h1>
);

/**
* Implement
* - Admin(AdminComponent)
* - User(UserComponent)
* Line 41 & 42
*/
const MainRouter = () => (
<Router>
<Fragment>
<Navigation/>
<Route path="/" component={HomeComponent} exact={true} />
<Route path="/admin" component={Admin(AdminComponent)} />
<Route path="/user" component={User(UserComponent)} />
</Fragment>
</Router>
);

export default MainRouter;

kemudian simpan, atau boleh melihat potongan gambar gerak dibawah ini.

terima kasih telah membaca dan melihat gambar geraknya juga yah, jika ada penjelasan yang kurang di mengerti silahkan berikan komentar atau pertanyaan di kolom response dibawah (Hal ini sangat baik untuk mencapai penulisan yang baik pula dan berbagi ilmunya lebih mudah untuk di pahami)

:Cheers:

  • Regards, Adysurya@valutac.com

Editor (RA)