Redux: Bagaimana cara memotong boilerplate saat memperbarui entitas bersarang?

Dibuat pada 3 Nov 2015  ·  32Komentar  ·  Sumber: reduxjs/redux

Jadi saya memiliki struktur bersarang ini di bawah negara saya

state = {
   plans: [
    {title: 'A', exercises: [{title: 'exe1'}, {title: 'exe2'},{title: 'exe3'}]},
    {title: 'B', exercises: [{title: 'exe5'}, {title: 'exe1'},{title: 'exe2'}]}
   ]
}

Saya mencoba membuat pengurangan yang tidak mengubah keadaan sebelumnya, tetapi itu sampai pada titik di mana saya menghabiskan lebih banyak waktu untuk mencari tahu bagaimana melakukan ini kemudian mengkodekan sisa aplikasi, itu menjadi sangat membuat frustrasi.

Misalnya, jika saya ingin menambahkan latihan kosong baru atau memperbarui latihan yang sudah ada, mutasi data akan saya lakukan:

state.plans[planIdx].exercises.push({})

state.plans[planIdx].exercises[exerciseIdx] = exercise

tapi apa pendekatan terbaik saya untuk melakukan hal yang sama dalam struktur bersarang ini? Saya telah membaca dokumen Redux dan juga bagian pemecahan masalah, tetapi yang terjauh yang saya dapatkan adalah memperbarui rencana, yang akan saya lakukan:

case 'UPDATE_PLAN':
    return {
      ...state,
      plans: [
      ...state.plans.slice(0, action.idx),
      Object.assign({}, state.plans[action.idx], action.plan),
      ...state.plans.slice(action.idx + 1)
      ]
    };

Apakah tidak ada cara yang lebih cepat untuk menangani ini? Bahkan jika saya harus menggunakan perpustakaan eksternal, atau setidaknya jika seseorang dapat menjelaskan kepada saya bagaimana menangani ini dengan lebih baik ...

Terima kasih!

docs question

Komentar yang paling membantu

Ya, kami sarankan untuk menormalkan data Anda.
Dengan cara ini Anda tidak perlu terlalu "dalam": semua entitas Anda berada di level yang sama.

Jadi negara bagian Anda akan terlihat seperti ini

{
  entities: {
    plans: {
      1: {title: 'A', exercises: [1, 2, 3]},
      2: {title: 'B', exercises: [5, 1, 2]}
     },
    exercises: {
      1: {title: 'exe1'},
      2: {title: 'exe2'},
      3: {title: 'exe3'}
    }
  },
  currentPlans: [1, 2]
}

Pengurang Anda mungkin terlihat seperti ini

import merge from 'lodash/object/merge';

const exercises = (state = {}, action) => {
  switch (action.type) {
  case 'CREATE_EXERCISE':
    return {
      ...state,
      [action.id]: {
        ...action.exercise
      }
    };
  case 'UPDATE_EXERCISE':
    return {
      ...state,
      [action.id]: {
        ...state[action.id],
        ...action.exercise
      }
    };
  default:
    if (action.entities && action.entities.exercises) {
      return merge({}, state, action.entities.exercises);
    }
    return state;
  }
}

const plans = (state = {}, action) => {
  switch (action.type) {
  case 'CREATE_PLAN':
    return {
      ...state,
      [action.id]: {
        ...action.plan
      }
    };
  case 'UPDATE_PLAN':
    return {
      ...state,
      [action.id]: {
        ...state[action.id],
        ...action.plan
      }
    };
  default:
    if (action.entities && action.entities.plans) {
      return merge({}, state, action.entities.plans);
    }
    return state;
  }
}

const entities = combineReducers({
  plans,
  exercises
});

const currentPlans = (state = [], action) {
  switch (action.type) {
  case 'CREATE_PLAN':
    return [...state, action.id];
  default:
    return state;
  }
}

const reducer = combineReducers({
  entities,
  currentPlans
});

Jadi apa yang terjadi disini? Pertama, perhatikan bahwa status dinormalisasi. Kami tidak pernah memiliki entitas di dalam entitas lain. Sebaliknya, mereka merujuk satu sama lain dengan ID. Jadi setiap kali beberapa objek berubah, hanya ada satu tempat yang perlu diperbarui.

Kedua, perhatikan bagaimana kita bereaksi terhadap CREATE_PLAN dengan menambahkan entitas yang sesuai di plans reducer _and_ dengan menambahkan ID-nya ke reducer currentPlans . Ini penting. Dalam aplikasi yang lebih kompleks, Anda mungkin memiliki hubungan, misalnya plans reducer dapat menangani ADD_EXERCISE_TO_PLAN dengan cara yang sama dengan menambahkan ID baru ke array di dalam rencana. Tetapi jika latihan itu sendiri diperbarui, _tidak perlu plans peredam untuk mengetahuinya, karena ID tidak berubah_.

Ketiga, perhatikan bahwa pengurang entitas ( plans dan exercises ) memiliki klausa khusus yang memperhatikan action.entities . Ini jika kami memiliki respons server dengan "kebenaran yang diketahui" yang ingin kami perbarui untuk mencerminkan semua entitas kami. Untuk mempersiapkan data Anda dengan cara ini sebelum mengirimkan tindakan, Anda dapat menggunakan normalizr . Anda dapat melihatnya digunakan dalam contoh "dunia nyata" di repo Redux.

Terakhir, perhatikan bagaimana reduksi entitas serupa. Anda mungkin ingin menulis fungsi untuk menghasilkannya. Itu di luar cakupan jawaban saya — terkadang Anda menginginkan lebih banyak fleksibilitas, dan terkadang Anda menginginkan lebih sedikit boilerplate. Anda dapat melihat kode penomoran halaman di pereduksi contoh "dunia nyata" untuk contoh menghasilkan pereduksi serupa.

Oh, dan saya menggunakan sintaks { ...a, ...b } . Ini diaktifkan di Babel tahap 2 sebagai proposal ES7. Ini disebut "operator penyebaran objek" dan setara dengan menulis Object.assign({}, a, b) .

Sedangkan untuk perpustakaan, Anda dapat menggunakan Lodash (berhati-hatilah agar tidak bermutasi, misalnya merge({}, a, b} benar tetapi merge(a, b) tidak), updeep , react-addons-update atau yang lainnya. Namun jika Anda merasa perlu melakukan pembaruan mendalam, itu mungkin berarti pohon status Anda tidak cukup datar, dan Anda tidak cukup menggunakan komposisi fungsional. Bahkan contoh pertama Anda:

case 'UPDATE_PLAN':
  return {
    ...state,
    plans: [
      ...state.plans.slice(0, action.idx),
      Object.assign({}, state.plans[action.idx], action.plan),
      ...state.plans.slice(action.idx + 1)
    ]
  };

dapat ditulis sebagai

const plan = (state = {}, action) => {
  switch (action.type) {
  case 'UPDATE_PLAN':
    return Object.assign({}, state, action.plan);
  default:
    return state;
  }
}

const plans = (state = [], action) => {
  if (typeof action.idx === 'undefined') {
    return state;
  }
  return [
    ...state.slice(0, action.idx),
    plan(state[action.idx], action),
    ...state.slice(action.idx + 1)
  ];
};

// somewhere
case 'UPDATE_PLAN':
  return {
    ...state,
    plans: plans(state.plans, action)
  };

Semua 32 komentar

Disarankan untuk menormalkan JSON bersarang seperti: https://github.com/gaearon/normalizr

Ya, kami sarankan untuk menormalkan data Anda.
Dengan cara ini Anda tidak perlu terlalu "dalam": semua entitas Anda berada di level yang sama.

Jadi negara bagian Anda akan terlihat seperti ini

{
  entities: {
    plans: {
      1: {title: 'A', exercises: [1, 2, 3]},
      2: {title: 'B', exercises: [5, 1, 2]}
     },
    exercises: {
      1: {title: 'exe1'},
      2: {title: 'exe2'},
      3: {title: 'exe3'}
    }
  },
  currentPlans: [1, 2]
}

Pengurang Anda mungkin terlihat seperti ini

import merge from 'lodash/object/merge';

const exercises = (state = {}, action) => {
  switch (action.type) {
  case 'CREATE_EXERCISE':
    return {
      ...state,
      [action.id]: {
        ...action.exercise
      }
    };
  case 'UPDATE_EXERCISE':
    return {
      ...state,
      [action.id]: {
        ...state[action.id],
        ...action.exercise
      }
    };
  default:
    if (action.entities && action.entities.exercises) {
      return merge({}, state, action.entities.exercises);
    }
    return state;
  }
}

const plans = (state = {}, action) => {
  switch (action.type) {
  case 'CREATE_PLAN':
    return {
      ...state,
      [action.id]: {
        ...action.plan
      }
    };
  case 'UPDATE_PLAN':
    return {
      ...state,
      [action.id]: {
        ...state[action.id],
        ...action.plan
      }
    };
  default:
    if (action.entities && action.entities.plans) {
      return merge({}, state, action.entities.plans);
    }
    return state;
  }
}

const entities = combineReducers({
  plans,
  exercises
});

const currentPlans = (state = [], action) {
  switch (action.type) {
  case 'CREATE_PLAN':
    return [...state, action.id];
  default:
    return state;
  }
}

const reducer = combineReducers({
  entities,
  currentPlans
});

Jadi apa yang terjadi disini? Pertama, perhatikan bahwa status dinormalisasi. Kami tidak pernah memiliki entitas di dalam entitas lain. Sebaliknya, mereka merujuk satu sama lain dengan ID. Jadi setiap kali beberapa objek berubah, hanya ada satu tempat yang perlu diperbarui.

Kedua, perhatikan bagaimana kita bereaksi terhadap CREATE_PLAN dengan menambahkan entitas yang sesuai di plans reducer _and_ dengan menambahkan ID-nya ke reducer currentPlans . Ini penting. Dalam aplikasi yang lebih kompleks, Anda mungkin memiliki hubungan, misalnya plans reducer dapat menangani ADD_EXERCISE_TO_PLAN dengan cara yang sama dengan menambahkan ID baru ke array di dalam rencana. Tetapi jika latihan itu sendiri diperbarui, _tidak perlu plans peredam untuk mengetahuinya, karena ID tidak berubah_.

Ketiga, perhatikan bahwa pengurang entitas ( plans dan exercises ) memiliki klausa khusus yang memperhatikan action.entities . Ini jika kami memiliki respons server dengan "kebenaran yang diketahui" yang ingin kami perbarui untuk mencerminkan semua entitas kami. Untuk mempersiapkan data Anda dengan cara ini sebelum mengirimkan tindakan, Anda dapat menggunakan normalizr . Anda dapat melihatnya digunakan dalam contoh "dunia nyata" di repo Redux.

Terakhir, perhatikan bagaimana reduksi entitas serupa. Anda mungkin ingin menulis fungsi untuk menghasilkannya. Itu di luar cakupan jawaban saya — terkadang Anda menginginkan lebih banyak fleksibilitas, dan terkadang Anda menginginkan lebih sedikit boilerplate. Anda dapat melihat kode penomoran halaman di pereduksi contoh "dunia nyata" untuk contoh menghasilkan pereduksi serupa.

Oh, dan saya menggunakan sintaks { ...a, ...b } . Ini diaktifkan di Babel tahap 2 sebagai proposal ES7. Ini disebut "operator penyebaran objek" dan setara dengan menulis Object.assign({}, a, b) .

Sedangkan untuk perpustakaan, Anda dapat menggunakan Lodash (berhati-hatilah agar tidak bermutasi, misalnya merge({}, a, b} benar tetapi merge(a, b) tidak), updeep , react-addons-update atau yang lainnya. Namun jika Anda merasa perlu melakukan pembaruan mendalam, itu mungkin berarti pohon status Anda tidak cukup datar, dan Anda tidak cukup menggunakan komposisi fungsional. Bahkan contoh pertama Anda:

case 'UPDATE_PLAN':
  return {
    ...state,
    plans: [
      ...state.plans.slice(0, action.idx),
      Object.assign({}, state.plans[action.idx], action.plan),
      ...state.plans.slice(action.idx + 1)
    ]
  };

dapat ditulis sebagai

const plan = (state = {}, action) => {
  switch (action.type) {
  case 'UPDATE_PLAN':
    return Object.assign({}, state, action.plan);
  default:
    return state;
  }
}

const plans = (state = [], action) => {
  if (typeof action.idx === 'undefined') {
    return state;
  }
  return [
    ...state.slice(0, action.idx),
    plan(state[action.idx], action),
    ...state.slice(action.idx + 1)
  ];
};

// somewhere
case 'UPDATE_PLAN':
  return {
    ...state,
    plans: plans(state.plans, action)
  };

Alangkah baiknya mengubahnya menjadi resep.

Idealnya kita menginginkan contoh papan kanban.
Ini sempurna untuk entitas bersarang karena "jalur" mungkin memiliki "kartu" di dalamnya.

@ andre0799 atau Anda bisa menggunakan Immutable.js))

Idealnya kita menginginkan contoh papan kanban.

Saya menulis satu . Mungkin Anda bisa memangkasnya dan menyesuaikannya dengan keinginan Anda.

Immutable.js tidak selalu merupakan solusi yang baik. Ini menghitung ulang hash dari setiap node induk negara mulai dari node yang Anda ubah dan itu menjadi hambatan dalam kasus tertentu (ini bukan kasus yang sangat umum). Jadi idealnya Anda harus membuat beberapa tolok ukur sebelum mengintegrasikan Immutable.js ke dalam aplikasi Anda.

Terima kasih @gaearon atas jawaban Anda, penjelasan bagus!

Jadi, ketika Anda melakukan CREATE_PLAN Anda harus secara otomatis membuat latihan default dan menambahkannya. Bagaimana saya harus menangani kasus seperti ini? Haruskah saya memanggil 3 tindakan berturut-turut? CREATE_PLAN, CREATE_EXERCISE, ADD_EXERCISE_TO_PLAN Dari mana saya harus melakukan panggilan ini, jika demikian?

Jadi, ketika Anda melakukan CREATE_PLAN, Anda harus secara otomatis membuat latihan default dan menambahkannya. Bagaimana saya harus menangani kasus seperti ini?

Meskipun umumnya saya mendukung banyak pereduksi yang menangani tindakan yang sama, ini bisa menjadi terlalu rumit untuk entitas dengan hubungan. Memang, saya menyarankan untuk membuat model ini sebagai tindakan terpisah.

Anda dapat menggunakan middleware Redux Thunk untuk menulis pembuat tindakan yang memanggil keduanya:

function createPlan(title) {
  return dispatch => {
    const planId = uuid();
    const exerciseId = uuid();

    dispatch({
      type: 'CREATE_EXERCISE',
      id: exerciseId,
      exercise: {
        id: exerciseId,
        title: 'Default'
      }
    });

    dispatch({
      type: 'CREATE_PLAN',
      id: planId,
      plan: {
        id: planId,
        exercises: [exerciseId],
        title
      }
    });
  };
}

Kemudian, jika Anda menerapkan middleware Redux Thunk, Anda dapat memanggilnya secara normal:

store.dispatch(createPlan(title));

Jadi katakanlah saya memiliki editor posting di backend di suatu tempat dengan hubungan seperti itu (posting, penulis, tag, lampiran, dll).

Bagaimana cara menampilkan currentPosts mirip dengan array kunci currentPlans ? Apakah saya perlu memetakan setiap kunci di currentPosts ke objek yang sesuai di entities.posts dalam fungsi mapStateToProps ? Bagaimana dengan menyortir currentPosts ?

Apakah semua ini termasuk dalam komposisi peredam?

Saya melewatkan sesuatu di sini ...

Mengenai pertanyaan awal, saya yakin React Immutability Helpers diciptakan untuk tujuan itu

Bagaimana cara menampilkan currentPosts yang mirip dengan larik kunci currentPlans? Apakah saya perlu memetakan setiap kunci di currentPosts ke objek yang sesuai di entity.posts di fungsi mapStateToProps? Bagaimana dengan menyortir currentPosts?

Ini benar. Anda akan melakukan segalanya saat mengambil data. Silakan lihat contoh "keranjang belanja" dan "dunia nyata" yang disertakan dengan repo Redux.

Terima kasih, saya sudah mulai mendapatkan ide setelah membaca Computing Derived Data di docs.

Saya akan memeriksa contoh-contoh itu lagi. Saya mungkin tidak mengerti banyak tentang apa yang terjadi pada awalnya ketika saya membacanya.

@ andrizal

Komponen connect() ed memiliki dispatch dimasukkan sebagai prop secara default.

this.props.dispatch(createPlan(title));

Ini adalah pertanyaan penggunaan yang tidak berhubungan dengan utas ini. Lebih baik melihat contoh atau membuat pertanyaan StackOverflow untuk ini.

Saya setuju dengan Dan dengan menormalisasi data, dan meratakan struktur negara sebanyak mungkin. Ini dapat dimasukkan sebagai resep / praktik terbaik dalam dokumentasi, karena itu akan menyelamatkan saya dari sakit kepala.

Ketika saya membuat kesalahan dengan memiliki sedikit kedalaman dalam keadaan saya, saya membuat lib ini untuk membantu memperbaiki dan mengelola keadaan dalam dengan Redux: https://github.com/baptistemanson/immutable-path
Mungkin saya salah paham, tapi saya tertarik dengan tanggapan Anda. Semoga bisa membantu seseorang.

Ini sangat membantu. Terimakasih untuk semua.

Bagaimana Anda akan menambahkan latihan ke dalam rencana, menggunakan struktur yang sama seperti contoh dunia nyata? Misalkan menambahkan latihan mengembalikan entitas latihan yang baru dibuat, dengan bidang planId . Apakah mungkin menambahkan latihan baru ke rencana itu tanpa harus menulis peredam untuk rencana, dan mendengarkan secara khusus tindakan CREATE_EXERCISE ?

Diskusi dan informasi hebat di sini. Saya ingin menggunakan normalizr untuk proyek saya tetapi memiliki pertanyaan tentang cara menyimpan data yang diperbarui kembali ke server jauh. Terutama, apakah ada cara sederhana untuk mengembalikan bentuk yang dinormalisasi kembali ke bentuk bersarang yang disediakan oleh api jarak jauh setelah memperbarui? Ini penting ketika klien membuat perubahan dan perlu memberikannya kembali ke api jarak jauh di mana dia tidak memiliki kendali atas bentuk permintaan pembaruan.

Misalnya: klien mengambil data latihan bersarang -> klien menormalkannya dan menyimpannya di redux -> pengguna membuat perubahan pada data yang dinormalisasi di sisi klien -> klik pengguna simpan -> aplikasi klien mengubah data yang dinormalisasi yang diperbarui kembali ke bentuk bersarang sehingga dapat mengirimkannya ke server jauh -> klien mengirimkan ke server

Jika saya menggunakan normalizr apakah saya perlu menulis transformator khusus untuk langkah tebal atau apakah ada perpustakaan atau metode pembantu yang Anda rekomendasikan untuk ini? Setiap rekomendasi akan sangat dihargai.

Terima kasih

Ada sesuatu yang disebut https://github.com/gpbl/denormalizr tetapi saya tidak yakin seberapa dekat itu melacak pembaruan normalizr. Saya menulis normalizr dalam beberapa jam untuk aplikasi yang saya kerjakan, Anda dipersilakan untuk mencabangnya dan menambahkan denormalisasi 😄.

Keren, saya pasti akan melihat denormalisasi dan berkontribusi kembali ke proyek Anda begitu saya memiliki sesuatu. Kerja bagus selama beberapa jam ;-) Terima kasih telah menghubungi saya kembali.

Saya agak berada dalam situasi yang sama dengan mengubah data yang sangat bersarang, namun, saya telah menemukan solusi yang berfungsi menggunakan immutable.js.

Bolehkah jika saya menautkan ke pos StackOverflow yang saya buat untuk meminta umpan balik tentang solusi di sini?

Saya sedang menautkannya untuk saat ini, harap hapus postingan saya atau katakan jika tidak pantas untuk menautkan di sini:
http://stackoverflow.com/questions/37171203/manipulating-data-in-nested-arrays-in-redux-with-immutable-js

Saya telah melihat pendekatan ini direkomendasikan di masa lalu. Namun, pendekatan ini tampaknya tidak berfungsi dengan baik saat objek bersarang perlu dihapus. Dalam hal ini, peredam perlu memeriksa semua referensi ke objek dan menghapusnya, sebuah operasi yang akan menjadi O (n), sebelum bisa menghapus objek itu sendiri. Adakah yang mengalami masalah serupa dan menyelesaikannya?

@ariofrio : ah ... Saya bingung. Titik normalisasi adalah bahwa objek _tidak_ disimpan bersarang, dan hanya ada satu referensi ke item tertentu, sehingga mudah untuk memperbarui item itu. Sekarang, jika ada beberapa entitas lain yang "mereferensikan" item ini dengan ID, tentu, mereka juga perlu diperbarui, sama seperti jika semuanya tidak dinormalisasi.

Apakah ada kekhawatiran khusus yang Anda miliki, atau skenario bermasalah yang Anda hadapi?

Ini yang saya maksud. Katakanlah keadaan saat ini terlihat seperti:

{
  entities: {
    plans: {
      1: {title: 'A', exercises: [1, 2, 3]},
      2: {title: 'B', exercises: [5, 6]}
     },
    exercises: {
      1: {title: 'exe1'},
      2: {title: 'exe2'},
      3: {title: 'exe3'}
      5: {title: 'exe5'}
      6: {title: 'exe6'}
    }
  },
  currentPlans: [1, 2]
}

Dalam contoh ini, setiap latihan hanya dapat dirujuk oleh satu rencana. Saat pengguna mengklik "Hapus Latihan", pesannya mungkin terlihat seperti ini:

{type: "REMOVE_EXERCISE", payload: 2}

Tetapi untuk menerapkan ini dengan benar, seseorang perlu mengulang semua rencana, dan kemudian semua latihan dalam setiap rencana, untuk menemukan latihan yang mereferensikan latihan dengan id 2, untuk menghindari referensi yang menggantung. Ini adalah operasi O (n) yang saya khawatirkan.

Cara untuk menghindari hal ini adalah dengan memasukkan id rencana dalam muatan REMOVE_EXERCISE, tetapi pada titik ini, saya tidak melihat keuntungan dibandingkan menggunakan struktur bersarang. Jika kita menggunakan status bersarang, statusnya mungkin terlihat seperti:

{
   plans: [
    {title: 'A', exercises: [{title: 'exe1'}, {title: 'exe2'},{title: 'exe3'}]},
    {title: 'B', exercises: [{title: 'exe5'}, {title: 'exe6'}]}
   ]
}

Dan pesan untuk menghapus latihan mungkin terlihat seperti:

{type: "REMOVE_EXERCISE", payload: {plan_index: 0, exercise_index: 1}}

Beberapa pemikiran:

  • Anda dapat mempertahankan pencarian latihan terbalik ke rencana untuk menyederhanakan skenario itu. Demikian pula, apa yang perpustakaan Redux-ORM lakukan adalah menghasilkan otomatis "melalui tabel" untuk banyak jenis relasi. Jadi, dalam hal ini Anda akan memiliki "tabel" "PlanExercise" di toko Anda, yang akan berisi {id, planId, exerciseId} triplet. Tentu saja scan O (n), tapi yang langsung.
  • Operasi O (n) bukanlah hal yang buruk. Itu sepenuhnya tergantung pada seberapa besar N, faktor konstanta di depan suku, seberapa sering hal itu terjadi, dan apa lagi yang terjadi dalam aplikasi Anda. Mengulangi daftar 10 atau 15 item dan melakukan beberapa pemeriksaan kesetaraan pada klik tombol pengguna akan menjadi hal yang sama sekali berbeda daripada, katakanlah, mengulang daftar 10 juta item yang masuk ke sistem setiap 500 md dan melakukan beberapa operasi mahal untuk masing-masing. barang. Dalam kasus ini, kemungkinan besar bahkan memeriksa ribuan rencana tidak akan menjadi hambatan yang berarti secara signifikan.
  • Apakah ini masalah kinerja aktual yang Anda lihat, atau hanya melihat ke depan untuk kemungkinan masalah teoretis?

Pada akhirnya, status bersarang dan normalisasi adalah konvensi. Ada alasan bagus untuk menggunakan status normalisasi dengan Redux, dan mungkin ada alasan bagus untuk menjaga status Anda tetap normal. Pilih apa pun yang cocok untuk Anda :)

solusi saya seperti ini:

function deepCombinReducer(parentReducer, subReducer) {
    return function (state = parentReducer(void(0) /* get parent reducer initial state */, {}) {
        let finalState = {...state};

        for (var k in subReducer) {
          finalState[k] = subReducer(state[k], action);
        }

       return parentReducer(finalState, action);
    };
}

const parentReducer = function(state = {}, action) {
    return state;
}

const subReducer = function(state = [], action) {
    state = Immutable.fromJS(state).toJS();
    switch(action.type) {
       case 'ADD':
          state.push(action.sub);
           return state;
       default:
          return state;
   }
}

export default combineReducers({
   parent: deepCombinReducer(parentReducer, {
       sub: subReducer
   })
})

Kemudian, Anda bisa mendapatkan toko seperti ini:

{
    parent: {
       sub: []
    }
}

dispatch({
    type: 'ADD',
    sub: '123'
});

// the store will change to:
{
    parent: {
       sub: ['123']
    }
}

@smashercosmo immutable.js dengan status bertingkat yang dalam? Saya penasaran bagaimana caranya

@gaul

Kami tidak pernah memiliki entitas di dalam entitas lain.

Saya tidak mengerti. Kami memiliki setidaknya tiga tingkat bersarang di sini:

{
  entities: {
    plans: {
      1: {title: 'A', exercises: [1, 2, 3]},
      2: {title: 'B', exercises: [5, 1, 2]}
     },
    exercises: {
      1: {title: 'exe1'},
      2: {title: 'exe2'},
      3: {title: 'exe3'}
    }
  },
  currentPlans: [1, 2]
}

entities.plans[1] - three levels
entities.exercises[1] - three levels

Ini bukan objek bertingkat. Hanya satu tingkat.

{
   plans: [1,2, 3],
   exercises: [1,2,3],
   'so forth': [1,2,3]
}

@wzup : FYI, Dan tidak menghabiskan banyak waktu untuk melihat masalah Redux akhir-akhir ini - dia sudah cukup banyak mengerjakan React.

Arti "bersarang" di sini adalah ketika datanya sendiri bersarang, seperti contoh dari sebelumnya di utas:

{
   plans: [
    {title: 'A', exercises: [{title: 'exe1'}, {title: 'exe2'},{title: 'exe3'}]},
    {title: 'B', exercises: [{title: 'exe5'}, {title: 'exe6'}]}
   ]
}

Dalam contoh tersebut, satu-satunya cara untuk mengakses latihan "exe6" adalah menggali ke dalam struktur, seperti plans[1].exercises[2] .

Saya tertarik dengan pertanyaan @tmonte :

Bagaimana Anda akan menambahkan latihan ke dalam rencana, menggunakan struktur yang sama seperti contoh dunia nyata? Misalkan menambahkan latihan mengembalikan entitas latihan yang baru dibuat, dengan bidang planId. Apakah mungkin untuk menambahkan latihan baru ke rencana itu tanpa harus menulis peredam untuk rencana, dan mendengarkan secara khusus tindakan CREATE_EXERCISE?

Jika Anda memiliki banyak entitas, membuat satu peredam untuk setiap entitas bisa jadi menyakitkan, tetapi pendekatan ini bisa menyelesaikannya. Sejauh ini saya belum menemukan solusi untuk itu.

Saya sebelumnya menggunakan mergeWith daripada merge untuk lebih banyak fleksibilitas:

import mergeWith from 'lodash/mergeWith';

// Updates an entity cache in response to any action with `entities`.
function entities(state = {}, action) {
  // Here where we STORE or UPDATE one or many entities
  // So check if the action contains the format we will manage
  // wich is `payload.entities`
  if (action.payload && action.payload.entities) {
    // if the entity is already in the store replace
    // it with the new one and do not merge. Why?
    // Assuming we have this product in the store:
    //
    // products: {
    //   1: {
    //     id: 1,
    //     name: 'Awesome product name',
    //     rateCategory: 1234
    //   }
    // }
    //
    // We will updated with
    // products: {
    //   1: {
    //     id: 1,
    //     name: 'Awesome product name',
    //   }
    // }
    //
    // The result if we were using `lodash/merge`
    // notice the rate `rateCategory` hasn't changed:
    // products: {
    //   1: {
    //     id: 1,
    //     name: 'Awesome product name',
    //     rateCategory: 1234
    //   }
    // }
    // for this particular use case it's safer to use
    // `lodash/mergeWith` and skip the merge
    return mergeWith({}, state, action.payload.entities, (oldD, newD) => {
      if (oldD && oldD.id && oldD.id === newD.id) {
        return newD;
      }
      return undefined;
    });
  }

  // Here you could register other handlers to manipulate 
  // the entities
  switch (action.type) {
    case ActionTypes.SOME_ACTION:
      // do something;
    default:
      return state;
  }
}

const rootReducer = combineReducers({
  entities,
});
export default rootReducer;
Apakah halaman ini membantu?
0 / 5 - 0 peringkat

Masalah terkait

timdorr picture timdorr  ·  3Komentar

caojinli picture caojinli  ·  3Komentar

mickeyreiss-visor picture mickeyreiss-visor  ·  3Komentar

captbaritone picture captbaritone  ·  3Komentar

amorphius picture amorphius  ·  3Komentar