Pada bab Mendesain Bentuk Status, dokumen menyarankan untuk mempertahankan status Anda dalam objek yang dikunci oleh ID:
Simpan setiap entitas dalam objek yang disimpan dengan ID sebagai kunci, dan gunakan ID untuk mereferensikannya dari entitas lain, atau daftar.
Mereka melanjutkan ke negara bagian
Pikirkan status aplikasi sebagai database.
Saya sedang mengerjakan bentuk status untuk daftar filter, beberapa di antaranya akan terbuka (ditampilkan dalam popup), atau telah memilih opsi. Saat saya membaca "Anggap status aplikasi sebagai database", saya berpikir untuk menganggapnya sebagai respons JSON karena akan dikembalikan dari API (yang didukung oleh database).
Jadi saya memikirkannya sebagai
[{
id: '1',
name: 'View',
open: false,
options: ['10', '11', '12', '13'],
selectedOption: ['10'],
parent: null,
},
{
id: '10',
name: 'Time & Fees',
open: false,
options: ['20', '21', '22', '23', '24'],
selectedOption: null,
parent: '1',
}]
Namun, dokumen menyarankan format yang lebih mirip
{
1: {
name: 'View',
open: false,
options: ['10', '11', '12', '13'],
selectedOption: ['10'],
parent: null,
},
10: {
name: 'Time & Fees',
open: false,
options: ['20', '21', '22', '23', '24'],
selectedOption: null,
parent: '1',
}
}
Secara teori, itu tidak masalah selama datanya dapat diserialkan (di bawah judul "State") .
Jadi saya pergi dengan pendekatan array-of-objects dengan senang hati, sampai saya menulis reducer saya.
Dengan pendekatan object-keyed-by-id (dan penggunaan liberal sintaks spread), OPEN_FILTER
bagian reducer menjadi
switch (action.type) {
case OPEN_FILTER: {
return { ...state, { ...state[action.id], open: true } }
}
Sedangkan dengan pendekatan array-of-objects, itu lebih verbose (dan fungsi pembantu bergantung)
switch (action.type) {
case OPEN_FILTER: {
// relies on getFilterById helper function
const filter = getFilterById(state, action.id);
const index = state.indexOf(filter);
return state
.slice(0, index)
.concat([{ ...filter, open: true }])
.concat(state.slice(index + 1));
}
...
Jadi pertanyaan saya ada tiga:
1) Apakah kesederhanaan peredam motivasi untuk menggunakan pendekatan object-keyed-by-id? Apakah ada keuntungan lain dari bentuk keadaan itu?
dan
2) Sepertinya pendekatan object-keyed-by-id membuatnya lebih sulit untuk menangani JSON standar masuk / keluar untuk API. (Itulah mengapa saya menggunakan array objek di tempat pertama.) Jadi jika Anda menggunakan pendekatan itu, apakah Anda hanya menggunakan fungsi untuk mengubahnya bolak-balik antara format JSON dan format bentuk negara? Sepertinya kikuk. (Meskipun jika Anda menganjurkan pendekatan itu, apakah bagian dari alasan Anda bahwa itu kurang kikuk daripada peredam array-objek di atas?)
dan
3) Saya tahu Dan Abramov mendesain redux agar secara teoritis menjadi status-data-struktur agnostik (seperti yang disarankan oleh "Berdasarkan konvensi, status tingkat atas adalah objek atau koleksi nilai kunci lainnya seperti Peta, tetapi secara teknis dapat berupa apa saja type , " tekankan milikku). Tetapi mengingat hal di atas, apakah hanya "disarankan" untuk menyimpannya sebagai objek yang dikunci oleh ID, atau adakah poin nyeri tak terduga lainnya yang akan saya hadapi dengan menggunakan berbagai objek yang membuatnya sedemikian rupa sehingga saya harus membatalkannya merencanakan dan mencoba untuk tetap dengan objek yang dikunci oleh ID?
sumber
sort_by
?const sorted = _.sortBy(collection, 'attribute');
Jawaban:
T1: Kesederhanaan peredam adalah hasil dari tidak harus mencari melalui larik untuk menemukan entri yang benar. Keuntungannya tidak harus mencari melalui array. Penyeleksi dan pengakses data lainnya dapat dan sering mengakses item ini dengan
id
. Harus mencari melalui array untuk setiap akses menjadi masalah kinerja. Saat array Anda menjadi lebih besar, masalah kinerja memburuk dengan tajam. Selain itu, saat aplikasi Anda menjadi lebih kompleks, menampilkan dan memfilter data di lebih banyak tempat, masalahnya juga memburuk. Kombinasi tersebut bisa merugikan. Dengan mengakses item denganid
, waktu akses berubah dariO(n)
menjadiO(1)
, yang untukn
item besar (di sini array item) membuat perbedaan besar.Q2: Anda dapat menggunakan
normalizr
untuk membantu Anda dengan konversi dari API ke toko. Pada normalizr V3.1.0 Anda dapat menggunakan denormalisasi untuk pergi ke arah lain. Meskipun demikian, Apps sering kali lebih menjadi konsumen daripada produsen data dan oleh karena itu konversi ke toko biasanya dilakukan lebih sering.T3: Masalah yang akan Anda hadapi dengan menggunakan larik bukanlah banyak masalah dengan konvensi penyimpanan dan / atau ketidakcocokan, tetapi lebih banyak masalah kinerja.
sumber
Itulah ide kuncinya.
1) Memiliki objek dengan ID unik memungkinkan Anda untuk selalu menggunakan id tersebut saat mereferensikan objek, jadi Anda harus meneruskan jumlah data minimum antara tindakan dan reduksi. Ini lebih efisien daripada menggunakan array.find (...). Jika Anda menggunakan pendekatan array, Anda harus meneruskan seluruh objek dan itu bisa segera menjadi berantakan, Anda mungkin akan membuat ulang objek tersebut pada reduksi, tindakan, atau bahkan dalam wadah yang berbeda (Anda tidak menginginkannya). Tampilan akan selalu bisa mendapatkan objek lengkap meskipun peredam terkaitnya hanya berisi ID, karena saat memetakan status Anda akan mendapatkan koleksi di suatu tempat (tampilan mendapatkan seluruh status untuk memetakannya ke properti). Karena semua yang saya katakan, tindakan akhirnya memiliki jumlah parameter minimal, dan mengurangi jumlah informasi minimal, cobalah,
2) Koneksi ke API seharusnya tidak memengaruhi arsitektur penyimpanan dan reduksi Anda, itulah mengapa Anda harus bertindak, untuk menjaga pemisahan masalah. Cukup masukkan logika konversi Anda ke dalam dan ke luar API dalam modul yang dapat digunakan kembali, impor modul tersebut dalam tindakan yang menggunakan API, dan seharusnya itu saja.
3) Saya menggunakan array untuk struktur dengan ID, dan ini adalah konsekuensi tak terduga yang saya derita:
Saya akhirnya mengubah struktur data saya dan menulis ulang banyak kode. Anda telah diperingatkan, tolong jangan membuat diri Anda bermasalah.
Juga:
4) Sebagian besar koleksi dengan ID dimaksudkan untuk menggunakan ID sebagai referensi ke seluruh objek, Anda harus memanfaatkannya. Panggilan API akan mendapatkan ID dan parameter lainnya, begitu juga tindakan dan reduksi Anda.
sumber
Alasan utama Anda ingin menyimpan entitas dalam objek yang disimpan dengan ID sebagai kunci (juga disebut dinormalisasi ), adalah karena sangat rumit untuk bekerja dengan objek yang sangat bertingkat (yang biasanya Anda dapatkan dari REST API di aplikasi yang lebih kompleks) - baik untuk komponen dan reduksi Anda.
Agak sulit untuk mengilustrasikan manfaat dari status yang dinormalisasi dengan contoh Anda saat ini (karena Anda tidak memiliki struktur bersarang yang dalam ). Tapi katakanlah bahwa opsi (dalam contoh Anda) juga memiliki judul, dan dibuat oleh pengguna di sistem Anda. Itu akan membuat responsnya terlihat seperti ini:
[{ id: 1, name: 'View', open: false, options: [ { id: 10, title: 'Option 10', created_by: { id: 1, username: 'thierry' } }, { id: 11, title: 'Option 11', created_by: { id: 2, username: 'dennis' } }, ... ], selectedOption: ['10'], parent: null, }, ... ]
Sekarang katakanlah Anda ingin membuat komponen yang menampilkan daftar semua pengguna yang telah membuat opsi. Untuk melakukan itu, pertama-tama Anda harus meminta semua item, lalu mengulang setiap opsinya, dan terakhir mendapatkan create_by.username.
Solusi yang lebih baik adalah menormalkan respons menjadi:
results: [1], entities: { filterItems: { 1: { id: 1, name: 'View', open: false, options: [10, 11], selectedOption: [10], parent: null } }, options: { 10: { id: 10, title: 'Option 10', created_by: 1 }, 11: { id: 11, title: 'Option 11', created_by: 2 } }, optionCreators: { 1: { id: 1, username: 'thierry', }, 2: { id: 2, username: 'dennis' } } }
Dengan struktur ini, jauh lebih mudah, dan lebih efisien, untuk membuat daftar semua pengguna yang telah membuat opsi (kami mengisolasi mereka di entity.optionCreators, jadi kami hanya perlu mengulang daftar itu).
Ini juga cukup mudah untuk ditampilkan, misalnya nama pengguna yang telah membuat opsi untuk item filter dengan ID 1:
entities .filterItems[1].options .map(id => entities.options[id]) .map(option => entities.optionCreators[option.created_by].username)
Respons JSON dapat dinormalisasi menggunakan mis . Normalizr .
Ini mungkin merupakan rekomendasi untuk aplikasi yang lebih kompleks dengan banyak respons API yang sangat bertingkat. Dalam contoh khusus Anda, itu tidak terlalu penting.
sumber
map
mengembalikan tidak terdefinisi seperti di sini , jika sumber daya diambil secara terpisah, membuatnyafilter
terlalu rumit. Apakah ada solusinya?