Indonesian (Bahasa Indonesia) translation by Febri Ardian Sanjoyo (you can also view the original English article)
High-Order Components (HOCs) adalah teknik yang menarik dalam React digunakan untuk refactor komponen serupa yang berbagi logika hampir sama. Saya tahu bahwa itu terdengar abstrak dan maju. Namun, ini adalah pola arsitektur yang tidak spesifik untuk React, dan karenanya Anda dapat menggunakan pendekatan untuk melakukan banyak hal.
Misalnya, Anda dapat menggunakannya untuk menambahkan indikator pemuatan ke komponen tertentu tanpa mengutak-atik komponen asli, atau Anda bisa menyembunyikan alat bantu komponen untuk membuatnya kurang verbose. Aplikasinya banyak, dan saya sudah mencoba meliput sebagian besar dari itu dalam tutorial ini.
Ada beberapa tutorial lain yang mengajarkan Anda tentang HOC, tetapi kebanyakan dari mereka dimaksudkan untuk pengembang React lanjutan. Ketika saya mulai belajar React, saya kesulitan memahami konsep komponen tingkat tinggi dan bagaimana saya bisa memasukkan HOC dalam proyek saya untuk menulis kode yang lebih baik. Artikel ini akan menjelaskan semua yang perlu Anda ketahui tentang HOC dari awal hingga akhir.
Ikhtisar
Tutorial ini dibagi menjadi tiga bagian. Bagian pertama akan berfungsi sebagai pengantar konsep komponen tingkat tinggi. Di sini, kita akan berbicara tentang sintaks yang perlu Anda ketahui sebelum melihat fungsi tingkat tinggi dan HOC. Bagian kedua adalah bagian paling menarik dari seri ini di mana Anda akan melihat contoh-contoh praktis dari HOC. Kami akan menggunakan HOC untuk membuat form, otorisasi, dan banyak hal lainnya.
Di bagian ketiga dari tutorial ini, kita akan lebih fokus pada praktik terbaik dan hal-hal yang perlu dipertimbangkan saat menerapkan hight-order components. Kami juga akan melihat sekilas pola alternatif untuk sharing kode di React, seperti alat peraga Render.
Sebelum memulai, mungkin ada baiknya untuk melihat tutorial tentang komponen Stateful vs. Stateless untuk memahami arsitektur komponen React lebih baik.
ES6 Syntax Cheatsheet
Kita akan segera mengotori tangan kita. Tetapi sebelum kita melakukannya, inilah beberapa hal yang menurut saya perlu Anda ketahui. Saya lebih suka menggunakan sintaks ES6 sedapat mungkin, dan itu bekerja hebat dengan HOC. Sebagai seorang pemula, HOC masuk akal, tetapi beberapa sintaks ES6 tidak. Jadi saya merekomendasikan untuk melalui bagian ini sekali, dan Anda dapat kembali ke sini nanti untuk referensi.
Fungsi Panah
Fungsi panah adalah ekspresi fungsi biasa, tetapi dengan sintaks yang lebih pendek. Mereka paling cocok untuk fungsi non-method, dan itulah yang kami sangat tertarik. Berikut ini beberapa contoh untuk Anda mulai:
Fungsi Tanpa Parameter
1 |
/* Functions without parameters */
|
2 |
function () { |
3 |
return "This is a function expression"; |
4 |
}
|
5 |
|
6 |
// is equivalent to
|
7 |
|
8 |
() => { |
9 |
return "This is an arrow function expression" |
10 |
}
|
11 |
|
12 |
// or
|
13 |
|
14 |
() => "Arrow with a shorter syntax" |
Fungsi Dengan Satu Parameter
1 |
/* Function with a single parameter */
|
2 |
|
3 |
function (param) { |
4 |
return { title: "This function accepts a parameter and returns an object", |
5 |
params: param} |
6 |
}
|
7 |
|
8 |
// is syntax-equivalent to
|
9 |
|
10 |
param => { |
11 |
return { title: "This arrow function accepts a single parameter", |
12 |
params: param } |
13 |
}
|
14 |
Fungsi Dengan Beberapa Parameter
1 |
/* Function with multiple parameters */
|
2 |
|
3 |
function (param1, param2) { |
4 |
return { title: "This function accepts multiple parameters", |
5 |
params: [param1,param2]} |
6 |
}
|
7 |
|
8 |
// is syntax-equivalent to
|
9 |
|
10 |
(param1, param2) => { |
11 |
return {title: "Arrow function with multiple parameters", |
12 |
params: [param1, param2] |
13 |
}
|
14 |
}
|
15 |
|
16 |
// or
|
17 |
|
18 |
(param1, param2) => ({ |
19 |
title: "Arrow function with multiple parameters", |
20 |
params: [param1, param2] |
21 |
})
|
Currying dalam Pemrograman Fungsional
Meskipun namanya menunjukkan bahwa itu ada hubungannya dengan hidangan eksotis dari masakan India yang populer, itu tidak. Currying membantu Anda memecah fungsi yang mengambil banyak argumen ke dalam serangkaian fungsi yang mengambil satu argumen pada suatu waktu. Berikut ini contohnya:
1 |
//Usual sum function
|
2 |
const sum = (a, b) => a + b |
3 |
|
4 |
//Curried sum function
|
5 |
const curriedSum = function (a) { |
6 |
return function (b) { |
7 |
return a+b |
8 |
}
|
9 |
|
10 |
//Curried sum function using arrow syntax
|
11 |
const curriedSum = a => b => a+b |
12 |
|
13 |
curriedSum(5)(4) |
14 |
//9
|
Fungsi hanya menerima satu argumen dan mengembalikan fungsi yang mengambil argumen lain, dan ini berlanjut sampai semua argumen dipenuhi.
1 |
curriedSum
|
2 |
// (a) => (b) => a+b
|
3 |
|
4 |
curriedSum(4) |
5 |
|
6 |
// (b) => 4+b
|
7 |
|
8 |
curriedSum(4)(5) |
9 |
|
10 |
//4+5
|
Istilah yang terkait erat disebut aplikasi parsial. Aplikasi parsial berhubungan dengan membuat fungsi baru dengan mengisi sebagian argumen dari fungsi yang ada. Fungsi yang baru dibuat akan memiliki arity (yang diterjemahkan ke jumlah argumen) kurang dari fungsi aslinya.
Spread Syntax
Spread operators menyebarkan isi dari array, string, atau ekspresi objek. Berikut adalah daftar hal-hal yang dapat Anda lakukan dengan spread operator
Spread Sintax dalam Function Calls
1 |
/*Spread Syntax in Function Calls */
|
2 |
const add = (x,y,z) => x+y+z |
3 |
|
4 |
const args = [1,2,3] |
5 |
|
6 |
add(...args) |
7 |
// 6
|
8 |
Spread Sintax dalam Array Literals
1 |
/* Spread in Array Literals */ |
2 |
|
3 |
const twoAndThree = ['two', 'three']; |
4 |
const numbers = ['one', ...twoAndThree, 'four', 'five']; |
5 |
// ["one", "two", "three", "four", "five"] |
6 |
Spread Syntax in Object Literals
1 |
/* Spread in Object Literals */
|
2 |
|
3 |
const contactName = { |
4 |
name: { |
5 |
first: "Foo", |
6 |
middle: "Lux", |
7 |
last: "Bar" |
8 |
}
|
9 |
}
|
10 |
const contactData = { |
11 |
email: "fooluxbar@example.com", |
12 |
phone: "1234567890" |
13 |
}
|
14 |
|
15 |
const contact = {...contactName, ...contactData} |
16 |
/* {
|
17 |
name: {
|
18 |
first: "Foo",
|
19 |
middle: "Lux",
|
20 |
last: "Bar"
|
21 |
}
|
22 |
email: "fooluxbar@example.com"
|
23 |
phone: "1234567890"
|
24 |
}
|
25 |
|
26 |
*/
|
27 |
Saya
pribadi menyukai cara di mana tiga titik dapat memudahkan Anda untuk
melewati alat peraga yang ada ke komponen anak atau membuat alat peraga
baru.
Operator Spread dalam React
1 |
const ParentComponent = (props) => { |
2 |
const newProps = { foo: 'default' }; |
3 |
|
4 |
return ( |
5 |
<ChildComponent |
6 |
{...props} {...newProps} |
7 |
/> |
8 |
)
|
9 |
}
|
Sekarang kita tahu sintaks ES6 dasar untuk membangun HOC, mari kita lihat apa itu.
Higher-Order Functions
Apakah higher-order function itu? Wikipedia memiliki definisi yang jelas:
Dalam matematika dan ilmu komputer, higher-order function (juga fungsional, bentuk fungsional atau functor) adalah fungsi yang baik mengambil satu atau lebih fungsi sebagai argumen atau mengembalikan fungsi sebagai hasilnya atau keduanya.
Anda mungkin pernah menggunakan fungsi tingkat tinggi dalam JavaScript sebelum dalam satu bentuk atau lainnya karena itulah cara JavaScript bekerja. Melewati fungsi anonim atau panggilan balik sebagai argumen atau fungsi yang mengembalikan fungsi lain — semua ini berada di bawah fungsi hight-order. Kode di bawah ini menciptakan fungsi kalkulator yang merupakan tatanan yang lebih tinggi di alam.
1 |
const calculator = (inputFunction) => |
2 |
(...args) => { |
3 |
|
4 |
const resultValue = inputFunction(...args); |
5 |
console.log(resultValue); |
6 |
|
7 |
return resultValue; |
8 |
}
|
9 |
|
10 |
const add = (...all) => { |
11 |
return all.reduce( (a,b) => a+b,0) ; |
12 |
|
13 |
}
|
14 |
|
15 |
|
16 |
const multiply = (...all) => { |
17 |
return all.reduce((a,b)=> a*b,1); |
18 |
|
19 |
}
|
Mari kita lihat lebih dalam ini. Calculator()
menerima fungsi sebagai input dan mengembalikan fungsi lain — ini sangat cocok dengan definisi kita tentang fungsi hight order. Karena kami telah menggunakan sintaks parameter sisanya, fungsi yang dikembalikan mengumpulkan semua argumennya di dalam array.
Kemudian, fungsi input dipanggil dengan semua argumen yang dilewatkan, dan hasilnya dicatat ke konsol. Jadi kalkulator adalah fungsi yang lebih kari, lebih tinggi, dan Anda dapat menggunakan kalkulator seperti ini:
1 |
calculator(multiply)(2,4); |
2 |
// returns 8
|
3 |
|
4 |
calculator(add)(3,6,9,12,15,18); |
5 |
// returns 63
|
Hubungkan fungsi seperti add()
atau multiply()
dan sejumlah parameter, dan calculator()
akan mengambilnya dari sana. Jadi calculator adalah wadah yang memperluas fungsi add()
dan multiply()
. Ini memberi kita kemampuan untuk menangani masalah pada tingkat yang lebih tinggi atau lebih abstrak. Sepintas, manfaat dari pendekatan ini termasuk:
- Kode dapat digunakan kembali di beberapa fungsi.
- Anda dapat menambahkan fungsionalitas tambahan umum untuk semua operasi aritmatika di container level.
- Ini lebih mudah dibaca, dan tujuan programmer lebih baik diungkapkan.
Sekarang kita memiliki gagasan yang bagus tentang fungsi tingkat tinggi, mari kita lihat apa yang bisa dilakukan oleh higher-order components.
Higher-Order Components
higher-order component adalah fungsi yang menerima komponen sebagai argumen dan mengembalikan versi tambahan dari komponen itu.
1 |
(InputComponent) => { |
2 |
return ExtendedComponent |
3 |
}
|
4 |
|
5 |
// or alternatively
|
6 |
|
7 |
InputComponent => ExtendedComponent |
8 |
The ExtendedComponent
menyusun InputComponent
. The ExtendedComponent
seperti wadah. Ini menjadikan InputComponent
, tetapi karena kita mengembalikan komponen baru, itu menambahkan lapisan tambahan abstraksi. Anda dapat menggunakan lapisan ini untuk menambahkan status, perilaku, atau bahkan style. Anda bahkan dapat memutuskan untuk tidak membuat InputComponent
sama sekali jika Anda menginginkan — HOC mampu melakukan itu dan banyak lagi.
Gambar di bawah ini harus membersihkan suasana kebingungan jika ada.



Cukup dengan teori — mari kita dapatkan kodenya. Berikut ini contoh HOC yang sangat sederhana yang membungkus komponen input di sekitar tag <div>
. Mulai sekarang, saya akan mengacu pada InputComponent
sebagai WrappedComponent
karena itulah konvensi. Namun, Anda dapat menyebutnya apa pun yang Anda inginkan.
1 |
/* The `with` prefix for the function name is a naming convention.
|
2 |
You can name your function anything you want as long as it's meaningful
|
3 |
*/
|
4 |
|
5 |
const withGreyBg = WrappedComponent => class NewComponent extends Component { |
6 |
|
7 |
const bgStyle = { |
8 |
backgroundColor: 'grey', |
9 |
};
|
10 |
|
11 |
render() { |
12 |
return ( |
13 |
<div className="wrapper" style={bgStyle}> |
14 |
|
15 |
<WrappedComponent {...this.props} /> |
16 |
</div> |
17 |
);
|
18 |
}
|
19 |
};
|
20 |
|
21 |
const SmallCardWithGreyBg = withGreyBg(SmallCard); |
22 |
const BigCardWithGreyBg = withGreyBg(BigCard); |
23 |
const HugeCardWithGreyBg = withGreyBg(HugeCard); |
24 |
|
25 |
class CardsDemo extends Component { |
26 |
render() { |
27 |
<SmallCardWithGreyBg {...this.props} /> |
28 |
<BigCardWithGreyBg {...this.props} /> |
29 |
<HugeCardWithGreyBg {...this.props /> |
30 |
}
|
31 |
}
|
Fungsi withGreyBg
mengambil komponen sebagai input dan mengembalikan komponen baru. Alih-alih langsung menyusun komponen-komponen Card dan melampirkan tag style ke masing-masing komponen, kita membuat HOC yang melayani tujuan ini. Higher-order component membungkus komponen asli dan menambahkan tag <div>
di sekelilingnya. Perlu dicatat bahwa Anda harus secara manual mewariskan properti di sini pada dua tingkat. Kita belum melakukan sesuatu yang wah, tapi inilah penampilan HOC yang normal. Gambar di bawah ini menunjukkan contoh withGreyBg()
secara lebih terperinci.



Meskipun ini mungkin tidak tampak sangat berguna saat ini, manfaatnya tidak sepele. Pertimbangkan skenario ini. Anda menggunakan React router, dan Anda perlu menjaga beberapa rute agar terlindungi — jika pengguna tidak diautentikasi, semua permintaan ke rute ini harus dialihkan ke /login
. Alih-alih menduplikasi kode otentikasi, kita dapat menggunakan HOC untuk secara efektif mengelola rute yang dilindungi. Ingin tahu bagaimana caranya? Kami akan membahasnya dan banyak lagi di tutorial selanjutnya.
Catatan: Ada fitur yang diusulkan dalam ECMAScript yang disebut dekorator yang memudahkan penggunaan HOC. Namun, ini masih merupakan fitur eksperimental, jadi saya memutuskan untuk tidak menggunakannya dalam tutorial ini. Jika Anda menggunakan creat-react-app, Anda harus keluar terlebih dahulu untuk menggunakan dekorator. Jika Anda menjalankan versi terbaru Babel (Babel 7), Anda hanya perlu
menginstal babel-preset-stage-0
dan kemudian menambahkannya ke daftar plugin di webpack.config.dev.js Anda sebagai berikut.
1 |
// Process JS with Babel.
|
2 |
{
|
3 |
test: /\.(js|jsx|mjs)$/, |
4 |
include: paths.appSrc, |
5 |
loader: require.resolve('babel-loader'), |
6 |
options: { |
7 |
|
8 |
// This is a feature of `babel-loader` for webpack (not Babel itself).
|
9 |
// It enables caching results in ./node_modules/.cache/babel-loader/
|
10 |
// directory for faster rebuilds.
|
11 |
cacheDirectory: true, |
12 |
presets: ['stage-0'] |
13 |
},
|
Ringkasan
Dalam tutorial ini, kami belajar konsep dasar HOC. HOC adalah teknik populer untuk membangun komponen yang dapat digunakan kembali. Kita mulai dengan diskusi tentang sintaks ES6 dasar sehingga akan lebih mudah bagi Anda untuk terbiasa dengan fungsi panah dan menulis kode JavaScript modern.
Kita kemudian melihat higher-order function dan cara kerjanya. Akhirnya, kita menyentuh higher-order components dan menciptakan HOC dari awal.
Selanjutnya, kita akan membahas teknik HOC yang berbeda dengan contoh-contoh praktis. Tetap disini hingga saat itu. Bagi pengalaman anda di bagian komentar.