Pendekatan Implementasi ReactJS RBAC

Entri


Halo pembaca yang budiman!

Beberapa waktu (sekitar setahun) yang lalu, saya dihadapkan dengan kebutuhan untuk membuat komponen secara kondisional di ReactJS, tergantung pada hak pengguna saat ini. Hal pertama yang saya mulai mencari solusi yang sudah jadi dan "praktik terbaik." Artikel " Otorisasi berbasis peran dalam React " paling terkesan dengan penggunaan Komponen Tingkat Tinggi (HOC). Tetapi, sayangnya, saya tidak menemukan solusi yang memuaskan saya.

Ternyata, bagaimanapun juga, dia melewatkan sesuatu ...
... atau tidak tahu tentang keberadaan konteks. Pada saat penulisan, saya menemukan jawaban yang bagus di stackoverflow . Saya berakhir dengan solusi yang sangat mirip.

Pada saat itu, saya sedikit terbiasa dengan reaksi-reduks-hubungkan (modul npm), dan saya sangat terhubung dengan pendekatan dekorasi yang digunakan dalam fungsi hubungkan. Analisis terperinci dari perangkat penghubung dapat ditemukan di sini .

Deskripsi Solusi


Pertama, Anda perlu menentukan informasi minimal apa yang diperlukan untuk membuat keputusan tentang rendering komponen. Jelas, untuk rendering perlu memenuhi beberapa kondisi (sebagai opsi, ada beberapa jenis hak - misalnya, hak untuk menambahkan pengguna baru). Kami menyebut kondisi ini persyaratan (atau persyaratan dalam bahasa Inggris). Untuk memahami apakah persyaratan telah dipenuhi, kita dapat berdasarkan pada serangkaian hak pengguna saat ini - kredensial . Artinya, cukup untuk mendefinisikan suatu fungsi:

function isSatisfied(requirement, credentials) { if (...) { return false; } return true; } 

Sekarang kami telah lebih atau kurang memutuskan kondisi render. Bagaimana cara menggunakannya?

1. Kita bisa menggunakan pendekatan dahi:

 const requirement = {...}; class App extends Component { render() { const {credentials} = this.props; return isSatisfied(requirement, credentials) && <TargetComponent>; } } 

2. Kita dapat melangkah lebih jauh, dan membungkus komponen target dengan komponen lain, yang akan melakukan verifikasi persyaratan:

 const requirement = {...}; class ProtectedTargetComponent extends Component { render() { const {credentials} = this.props; return ( isSatisfied(requirement, credentials) ? <TargetComponent {...this.props}> {this.props.children} </TargetComponent> : null ); } } class App extends Component { render() { const {credentials} = this.props; return <ProtectedTargetComponent/>; } } 

Menulis pembungkus secara manual untuk setiap komponen target cukup suram. Bagaimana kita bisa menyederhanakan ini?

3. Kita dapat menggunakan mekanisme HOC (dengan analogi dengan koneksi dari "react-redux-connect"):

 function protect(requirement, WrappedComponent) { return class extends Component { render() { const { credentials } = this.props; return ( isSatisfied(requirement, credentials) ? <WrappedComponent {...this.props}> {this.props.children} </WrappedComponent> : null ); } } } ... const requireAdmin = {...}; const AdminButton = protect(requireAdmin, Button); ... class App extends Component { render() { const {credentials} = this.props; return ( ... <AdminButton credentials={credentials}> Add user </AdminButton> ... ); } } 

Sudah lebih baik, tetapi masih celaka - Anda harus secara manual membuang kredensial melalui seluruh pohon komponen. Apa yang bisa dilakukan dengan ini? Adalah logis untuk mengasumsikan bahwa kredensial pengguna saat ini adalah objek global untuk seluruh aplikasi. Kemudian lagi reaksi-redux-connect datang untuk menyelamatkan. Setelah membaca artikel tentang perangkat modul ini, kami menemukan bahwa ia menggunakan beberapa konteks ReactJS.

4. Menggunakan mekanisme konteks, kita mendapatkan pendekatan akhir:

 const { Provider, Consumer } = React.createContext(); function protect(requirement, WrappedComponent) { return class extends Component { render() { return ( <Consumer> { credentials => isSatisfied(requirement, credentials) ? <WrappedComponent {...this.props}> {this.props.children} </WrappedComponent> : null } </Consumer> ); } } } ... const requireAdmin = {...}; const AdminButton = protect(requireAdmin, Button); ... class App extends Component { render() { const { credentials } = this.props; return ( <Provider value={credentials}> ... <AdminButton> Add user </AdminButton> ... </Provider> ); } } 

Kata penutup


Itu adalah penyimpangan singkat ke dalam ide itu sendiri. Berdasarkan ide ini, modul diimplementasikan ( github , npm ), yang menyediakan fitur yang lebih menarik dan lebih mudah ditanamkan (lihat README.md di github dan demo menggunakan modul).

Hanya karena alasan tertentu saya tidak bisa memasukkan paket npm yang dibuat ke dalam demo, jadi saya harus memasukkan kode modul di sana. Tetapi modul yang dipasang melalui npm install react-rbac-guard bekerja secara lokal (Chrome 69.0.3497.100). Saya menduga bahwa masalahnya ada dalam metode build - Saya baru saja menyalin file package.json dan webpack.config.prod.js dari modul dengan tujuan yang sama.

Karena saya bukan pengembang front-end, masih ada banyak pekerjaan yang belum selesai (kurangnya tes, tidak bisa dioperasi di https://codesandbox.io dan, mungkin, poin terlewatkan lainnya). Karena itu, jika ada komentar, saran atau permintaan tarik, maka selamat datang!

PPS: Semua komentar tentang ejaan, termasuk dalam README.md, silakan kirim pesan pribadi atau dalam bentuk permintaan tarik.

Source: https://habr.com/ru/post/id428143/


All Articles