Apa cara terbaik untuk menangani kesalahan pengambilan di react redux?

105

Saya memiliki satu peredam untuk Klien, satu lagi untuk AppToolbar dan beberapa lainnya ...

Sekarang katakanlah saya membuat tindakan pengambilan untuk menghapus klien, dan jika gagal saya memiliki kode di peredam Klien yang seharusnya melakukan beberapa hal, tetapi saya juga ingin menampilkan beberapa kesalahan global di AppToolbar.

Tetapi klien dan pengurang AppToolbar tidak berbagi bagian yang sama dari status dan saya tidak dapat membuat tindakan baru di peredam.

Jadi bagaimana saya bisa menunjukkan kesalahan global? Terima kasih

UPDATE 1:

Saya lupa menyebutkan bahwa saya menggunakan este devstack

PEMBARUAN 2: Saya menandai jawaban Eric sebagai benar, tetapi saya harus mengatakan bahwa solusi yang saya gunakan di este lebih seperti kombinasi jawaban Eric dan Dan ... Anda hanya perlu menemukan yang paling cocok untuk Anda dalam kode Anda .. .

Dusan Plavak
sumber
2
Anda mendapatkan banyak suara, dan itu mungkin karena Anda tidak memberikan banyak kode contoh. Pertanyaan Anda dan jawaban yang Anda dapatkan akan lebih membantu orang lain jika masalahnya diuraikan dengan lebih jelas.
acjay
Saya harus setuju dengan @acjay bahwa pertanyaan ini kurang dalam konteks. Saya telah menjawab di bawah ini (dengan contoh kode) dengan solusi umum, tetapi pertanyaan Anda dapat menggunakan beberapa perbaikan. Sepertinya Anda mungkin memiliki beberapa masalah terpisah. 1) Menangani tindakan / kesalahan asinkron. 2) Membagi status dengan tepat di pohon status redux Anda. 3) Mendapatkan komponen Anda data yang mereka butuhkan.
Erik Aybar
@ErikTheDeveloper terima kasih, jawaban Anda tampak hebat. Tapi Anda benar, saya lupa menyebutkan konteksnya. Saya mengedit pertanyaan saya, saya menggunakan este devstack dan sepertinya jawaban Anda tidak berlaku di sana sebagaimana adanya ...
Dusan Plavak

Jawaban:

116

Jika Anda ingin memiliki konsep "kesalahan global", Anda dapat membuat errorsperedam, yang dapat mendengarkan tindakan addError, removeError, dll .... Kemudian, Anda dapat menghubungkan ke pohon status Redux Anda di state.errorsdan menampilkannya di mana saja sesuai.

Ada beberapa cara untuk mendekati ini, tetapi gagasan umumnya adalah bahwa kesalahan / pesan global akan membutuhkan peredamnya sendiri untuk hidup sepenuhnya terpisah dari <Clients />/ <AppToolbar />. Tentu saja jika salah satu dari komponen ini membutuhkan akses, errorsAnda dapat errorsmenurunkannya sebagai penyangga di mana pun diperlukan.

Pembaruan: Contoh Kode

Berikut adalah salah satu contoh tampilan jika Anda meneruskan "kesalahan global" errorske tingkat teratas <App />dan merendernya secara bersyarat (jika ada kesalahan). Menggunakan react-reduxconnect untuk menghubungkan <App />komponen Anda ke beberapa data.

// App.js
// Display "global errors" when they are present
function App({errors}) {
  return (
    <div>
      {errors && 
        <UserErrors errors={errors} />
      }
      <AppToolbar />
      <Clients />
    </div>
  )
}

// Hook up App to be a container (react-redux)
export default connect(
  state => ({
    errors: state.errors,
  })
)(App);

Dan sejauh menyangkut pencipta tindakan, itu akan mengirimkan ( redux-thunk ) kegagalan sukses sesuai dengan respons

export function fetchSomeResources() {
  return dispatch => {
    // Async action is starting...
    dispatch({type: FETCH_RESOURCES});

    someHttpClient.get('/resources')

      // Async action succeeded...
      .then(res => {
        dispatch({type: FETCH_RESOURCES_SUCCESS, data: res.body});
      })

      // Async action failed...
      .catch(err => {
        // Dispatch specific "some resources failed" if needed...
        dispatch({type: FETCH_RESOURCES_FAIL});

        // Dispatch the generic "global errors" action
        // This is what makes its way into state.errors
        dispatch({type: ADD_ERROR, error: err});
      });
  };
}

Meskipun peredam Anda dapat dengan mudah mengelola serangkaian kesalahan, menambahkan / menghapus entri dengan tepat.

function errors(state = [], action) {
  switch (action.type) {

    case ADD_ERROR:
      return state.concat([action.error]);

    case REMOVE_ERROR:
      return state.filter((error, i) => i !== action.index);

    default:
      return state;
  }
}
Erik Aybar
sumber
1
Erik, saya memiliki sesuatu yang mirip dengan apa yang Anda sarankan di sini tetapi yang mengejutkan saya tidak pernah berhasil mendapatkan catchfungsi yang dipanggil jika someHttpClient.get('/resources')atau fetch('/resources')yang saya gunakan dalam pengembalian kode saya 500 Server Error. Apakah Anda memiliki pemikiran di luar kepala Anda di mana saya mungkin membuat kesalahan? Pada dasarnya, apa yang saya lakukan adalah fetchmengirimkan permintaan yang berakhir dengan di routesmana saya memanggil metode pada mongoosemodel saya untuk melakukan sesuatu yang sangat sederhana, seperti menambahkan teks atau menghapus teks dari DB.
Kevin Ghaboosi
2
Hai, saya datang ke sini dari pencarian Google - hanya ingin mengucapkan terima kasih atas contoh yang bagus .. Saya telah berjuang dengan masalah yang sama, dan ini luar biasa. Tentu saja solusinya adalah dengan mengintegrasikan kesalahan ke dalam penyimpanan. Mengapa saya tidak memikirkan itu ... tepuk tangan
Spock
2
Bagaimana cara menjalankan fungsi saat terjadi kesalahan? misalnya saya perlu menunjukkan toast / alert di UI, tidak membuat komponen Alert dengan memperbarui props dari komponen induk
Gianfranco P.
111

Jawaban Erik benar tetapi saya ingin menambahkan bahwa Anda tidak perlu melakukan tindakan terpisah untuk menambahkan kesalahan. Pendekatan alternatif adalah memiliki peredam yang menangani tindakan apa pun dengan suatu errorbidang . Ini adalah masalah pilihan dan kesepakatan pribadi.

Misalnya dari contoh Reduxreal-world yang memiliki penanganan error:

// Updates error message to notify about the failed fetches.
function errorMessage(state = null, action) {
  const { type, error } = action

  if (type === ActionTypes.RESET_ERROR_MESSAGE) {
    return null
  } else if (error) {
    return error
  }

  return state
}
Dan Abramov
sumber
Apakah ini berarti pada setiap permintaan yang berhasil kita harus meneruskan jenis RESET_ERROR_MESSAGE ke peredam errorMessage?
Dimi Mikadze
2
@DimitriMikadze tidak. Fungsi ini hanya peredam untuk status kesalahan. Jika Anda melewati RESET_ERROR_MESSAGE itu akan menghapus semua pesan kesalahan. Jika Anda tidak lulus dan tidak ada bidang kesalahan, itu hanya mengembalikan keadaan tidak berubah, jadi jika ada beberapa kesalahan dari tindakan sebelumnya, mereka masih akan ada setelah tindakan sukses ....
Dusan Plavak
Saya lebih suka pendekatan ini karena memungkinkan respons sebaris yang lebih alami saat konsumen menempelkan errormuatan tindakan. Terima kasih Dan!
Mike Perrenoud
1
Saya tidak bisa mengerti bagaimana ini bekerja. Selain dari contoh dunia nyata, apakah Anda memiliki isolasi dan dokumen / video yang menjelaskan hal ini? Ini adalah persyaratan inti yang cukup dari sebagian besar proyek, dan saya menemukan sedikit dokumentasi yang mudah dipahami tentang masalah ini. Terima kasih.
Matt Saunders
6
@MattSaunders Ketika mencoba memahaminya, saya menemukan kursus Redux oleh Dan sendiri (penjawab, yang sebenarnya adalah pencipta Redux), dengan bagian Menampilkan Pesan Kesalahan yang bersama dengan jawaban ini dan contoh dunia nyata mengantarkannya pulang saya. Semoga berhasil.
Agustín Lado
2

Pendekatan yang saat ini saya ambil untuk beberapa kesalahan spesifik (validasi input pengguna) adalah meminta sub-reduksi saya membuat pengecualian, menangkapnya di peredam root saya, dan melampirkannya ke objek tindakan. Lalu saya memiliki redux-saga yang memeriksa objek tindakan untuk kesalahan dan memperbarui pohon status dengan data kesalahan dalam kasus itu.

Begitu:

function rootReducer(state, action) {
  try {
    // sub-reducer(s)
    state = someOtherReducer(state,action);
  } catch (e) {
    action.error = e;
  }
  return state;
}

// and then in the saga, registered to take every action:
function *errorHandler(action) {
  if (action.error) {
     yield put(errorActionCreator(error));
  }
}

Dan kemudian menambahkan kesalahan ke pohon status seperti yang dijelaskan Erik.

Saya menggunakannya dengan hemat, tetapi itu membuat saya tidak harus menduplikasi logika yang secara sah termasuk dalam peredam (sehingga dapat melindungi dirinya sendiri dari keadaan tidak valid).

Gavin
sumber
1

tulis Middleware khusus untuk menangani semua kesalahan terkait api. Dalam hal ini kode Anda akan lebih bersih.

   failure/ error actin type ACTION_ERROR

   export default  (state) => (next) => (action) => {

      if(ACTION_ERROR.contains('_ERROR')){

       // fire error action
        store.dispatch(serviceError());

       }
}
Khalid Azam
sumber
1
Juga mempersulit debug IMHO
chrisjlee
2
Anda tidak perlu middleware untuk ini, Anda dapat menulis hal yang sama persis ifdi peredam
Juan Campa
Jika ada lebih dari 50 api maka Anda perlu menulis di mana-mana. Sebagai gantinya Anda dapat menulis middleware khusus untuk memeriksa kesalahan.
Shrawan
0

apa yang saya lakukan adalah saya memusatkan semua penanganan kesalahan yang berlaku pada setiap efek dasar

/**
 * central error handling
 */
@Effect({dispatch: false})
httpErrors$: Observable<any> = this.actions$
    .ofType(
        EHitCountsActions.HitCountsError
    ).map(payload => payload)
    .switchMap(error => {
        return of(confirm(`There was an error accessing the server: ${error}`));
    });
meanstack
sumber
-8

Anda dapat menggunakan klien HTTP axios. Ini sudah menerapkan fitur Interceptors. Anda bisa mencegat permintaan atau tanggapan sebelum ditangani saat itu atau menangkap.

https://github.com/mzabriskie/axios#interceptors

// Add a request interceptor
axios.interceptors.request.use(function (config) {
    // Do something before request is sent
    return config;
  }, function (error) {
    // Do something with request error
    return Promise.reject(error);
  });

// Add a response interceptor
axios.interceptors.response.use(function (response) {
    // Do something with response data
    return response;
  }, function (error) {
    // Do something with response error
    return Promise.reject(error);
  });

Phú Đỗ
sumber
Ya, tetapi Anda tidak mengirim apa pun ke redux?
Eino Mäkitalo
Pendekatan ini tidak buruk. Biasanya penyimpanan di redux adalah tunggal dan Anda dapat mengimpor penyimpanan di file interceptors axios dan menggunakan store.dispatch () untuk memicu tindakan apa pun. Ini adalah pendekatan kesatuan untuk menangani semua kesalahan api dalam sistem di 1 tempat
Rabu