Redux: apa idiom redux untuk menunggu beberapa panggilan asinkron?

Dibuat pada 14 Sep 2015  ·  30Komentar  ·  Sumber: reduxjs/redux

Ketika saya memulai komponen react, saya perlu membuat 2 panggilan http async berbeda dan menunggu hasilnya.
Idealnya 2 panggilan ini akan menempatkan datanya dalam 2 reduksi berbeda.

Saya memahami pola untuk membuat satu panggilan asinkron.
Namun, saya tidak melihat sampel yang bagus untuk membuat beberapa panggilan asinkron, meminta mereka mengeksekusi secara paralel dan menunggu _both_ hasilnya tiba.

Apa cara redux melakukan ini?

question

Komentar yang paling membantu

Saya berasumsi Anda menggunakan middleware Redux Thunk .

Nyatakan beberapa pembuat tindakan:

import 'babel-core/polyfill'; // so I can use Promises
import fetch from 'isomorphic-fetch'; // so I can use fetch()

function doSomething() {
  return dispatch => 
    fetch(
      '/api/something'
    ).then(
      response => response.json()
    ).then(
      json => dispatch({ type: DO_SOMETHING, json }),
      err => dispatch({ type: SOMETHING_FAILED, err })
    );
}

function doSomethingElse() {
  return dispatch => 
    fetch(
      '/api/something'
    ).then(
      response => response.json()
    ).then(
      json => dispatch({ type: DO_SOMETHING_ELSE, json }),
      err => dispatch({ type: SOMETHING_ELSE_FAILED, err })
    );
}

Perhatikan bagaimana saya membuat titik untuk mengembalikan Promise di bagian paling akhir dengan menjaga rantai sebagai nilai kembali dari fungsi di dalam pembuat tindakan thunk. Ini memungkinkan saya melakukan itu:

store.dispatch(doSomething()).then(() => {
  console.log('I did something');
});

Saat Anda menggunakan Redux Thunk, dispatch mengembalikan nilai yang dikembalikan dari fungsi yang Anda kembalikan dari pembuat tindakan thunk — dalam hal ini, Promise.

Ini memungkinkan Anda menggunakan kombinator seperti Promise.all() untuk menunggu penyelesaian beberapa tindakan:

Promise.all([
  store.dispatch(doSomething()),
  store.dispatch(doSomethingElse())
]).then(() => {
  console.log('I did everything!');
});

Namun lebih disukai untuk menentukan pembuat tindakan lain yang akan bertindak sebagai _komposisi_ dari pembuat tindakan sebelumnya, dan menggunakan Promise.all internal:

function doEverything() {
  return dispatch => Promise.all([
    dispatch(doSomething()),
    dispatch(doSomethingElse())
  ]);
}

Sekarang Anda tinggal menulis

store.dispatch(doEverything()).then(() => {
  console.log('I did everything!');
});

Selamat mengirim!

Semua 30 komentar

Saya berasumsi Anda menggunakan middleware Redux Thunk .

Nyatakan beberapa pembuat tindakan:

import 'babel-core/polyfill'; // so I can use Promises
import fetch from 'isomorphic-fetch'; // so I can use fetch()

function doSomething() {
  return dispatch => 
    fetch(
      '/api/something'
    ).then(
      response => response.json()
    ).then(
      json => dispatch({ type: DO_SOMETHING, json }),
      err => dispatch({ type: SOMETHING_FAILED, err })
    );
}

function doSomethingElse() {
  return dispatch => 
    fetch(
      '/api/something'
    ).then(
      response => response.json()
    ).then(
      json => dispatch({ type: DO_SOMETHING_ELSE, json }),
      err => dispatch({ type: SOMETHING_ELSE_FAILED, err })
    );
}

Perhatikan bagaimana saya membuat titik untuk mengembalikan Promise di bagian paling akhir dengan menjaga rantai sebagai nilai kembali dari fungsi di dalam pembuat tindakan thunk. Ini memungkinkan saya melakukan itu:

store.dispatch(doSomething()).then(() => {
  console.log('I did something');
});

Saat Anda menggunakan Redux Thunk, dispatch mengembalikan nilai yang dikembalikan dari fungsi yang Anda kembalikan dari pembuat tindakan thunk — dalam hal ini, Promise.

Ini memungkinkan Anda menggunakan kombinator seperti Promise.all() untuk menunggu penyelesaian beberapa tindakan:

Promise.all([
  store.dispatch(doSomething()),
  store.dispatch(doSomethingElse())
]).then(() => {
  console.log('I did everything!');
});

Namun lebih disukai untuk menentukan pembuat tindakan lain yang akan bertindak sebagai _komposisi_ dari pembuat tindakan sebelumnya, dan menggunakan Promise.all internal:

function doEverything() {
  return dispatch => Promise.all([
    dispatch(doSomething()),
    dispatch(doSomethingElse())
  ]);
}

Sekarang Anda tinggal menulis

store.dispatch(doEverything()).then(() => {
  console.log('I did everything!');
});

Selamat mengirim!

@gaearon Adakah alasan khusus Anda menyukai thunk lebih dari promise middleware? atau nasihat tentang kapan harus menggunakan apa? Terima kasih.

Tidak ada alasan khusus, saya hanya lebih mengenalnya.
Cobalah keduanya dan gunakan yang terbaik untuk Anda!

Dimengerti, terima kasih!

JFYI :)
screen shot 2015-09-14 at 11 06 42 am

jika Anda mau, Anda dapat melihatnya https://github.com/Cron-J/redux-async-transitions

@gaearon pertanyaan singkat - lihat contoh Anda di atas, di mana Anda menelepon:

store.dispatch(doSomething()).then(() => {
  console.log('I did something');
});

- sepertinya blok "Saya melakukan sesuatu" akan terkena bahkan jika doSomething ditolak - karena Anda tidak memunculkan kembali kesalahan dalam rantai doSomething setelah Anda menangkapnya. Apakah itu disengaja? Jika tidak - dan jika Anda akan mengembalikan kesalahan ke sana - apakah Anda akan menanganinya lagi di pemanggil?

Ini hanya sebuah contoh, yang saya maksud ini bukan referensi yang pasti, hanya sebagai cara untuk melihatnya. Aku tidak memikirkannya semenit pun.

Anda tahu cara membalas janji dari pencipta tindakan, sisanya terserah Anda. Apakah akan mengembalikan kesalahan, di mana menanganinya, semuanya terserah Anda. Ini sebenarnya bukan masalah Redux jadi saran saya sama baiknya dengan saran orang lain, dan dalam hal ini Anda memiliki lebih banyak konteks tentang apa yang ingin Anda bangun dan gaya yang Anda sukai daripada yang pernah saya miliki.

Keren Terimakasih! Pikir itu masalahnya, tetapi ingin memastikan tidak ada sihir yang saya abaikan.

Karena Redux dapat disesuaikan, dispatch nilai pengembalian tidak dijamin menjadi janji yang diharapkan.

Sebenarnya, saya mengalami satu masalah dalam kehidupan nyata sejak saya menggunakan redux-loop. Ini mengubah dispatch untuk mengembalikan Promise yang dibungkus (https://github.com/raisemarketplace/redux-loop/blob/master/modules/effects.js#L38). Jadi, kita tidak bisa bergantung pada nilai pengembalian dispatch .

Nah, seperti yang Dan katakan di atas di utas ini: ini _aplikasi Anda_, _you_ menulis pembuat tindakan Anda, jadi _Anda_ dapat memutuskan apa yang mereka kembalikan.

@markerikson Nilai kembali dispatch tidak dijamin akan menjadi nilai pengembalian dari pembuat tindakan, karena middleware dapat men-tweaknya.

Tapi, saya punya solusi. Alih-alih merantai then pada dispatch(actonCreator()) , kita dapat merantai actionCreator() .

Ya, middleware dapat mengubah banyak hal, tetapi maksud saya adalah _you_ menyiapkan middleware di aplikasi Anda sendiri :)

Hai @gaearon ,

function fetchDataBWhichDependsOnA() {
  return dispatch => {
    dispatch(fetchDataA())
      .then(() => {
        fetch('/api/get/data/B')
          .then(dispatch(receiveDataB(response.data)))
      })
  }
}

Ada 2 tindakan asinkron bersarang dalam cuplikan kode ini, mereka berfungsi tetapi saya mengalami masalah saat mengujinya. Saya dapat menguji fetchDataA() karena hanya memiliki 1 tingkat panggilan asinkron. Tetapi ketika saya mencoba menulis tes untuk fetchDataBWhichDependsOnA() , saya tidak dapat mengambil nilai terbaru dari blok store dalam then .

@timothytong Anda tidak membalas janji. Jika Anda mengembalikan hasil dari dispatch dan panggilan bertingkat fetch Anda seharusnya dapat menunggu dalam pengujian sampai seluruh rantai diselesaikan.

@johanneslumpe tangkapan yang bagus, terima kasih sobat! Sekarang yang tersisa hanyalah pertanyaan apakah ini dapat diterima atau tidak, berurusan dengan ketergantungan data dengan rantai pengambilan async?

Saya memiliki masalah yang sama dan menghabiskan 30 menit mencoba menemukan solusi. Saya belum menyelesaikan penerapan ini, tetapi saya merasa ini berada di jalur yang benar.

https://github.com/Sailias/sync-thunk

Ide saya adalah bahwa sebuah komponen dapat meminta status redux tertentu untuk diisi, jika tidak, itu harus mengacu pada peta untuk memanggil tindakan untuk mengisinya dan merantai mereka dengan then . Setiap tindakan dalam rantai tidak memerlukan data yang diteruskan, itu hanya dapat mengambil data dari keadaan karena tindakan dependen akan mengisinya.

@gaul

Promise.all([
  store.dispatch(doSomething()),
  store.dispatch(doSomethingElse())
]).then(() => {
  console.log('I did everything!');
});

Saya punya pertanyaan tentang ini. Maaf jika itu sangat mendasar.

Dalam skenario ini, akan ada dua pengiriman. Artinya, dua kali komponen akan dirender. Tetapi, bagaimana jika komponen menginginkan kedua data tersebut menunjukkan sesuatu yang berarti?

Jadi, dalam skenario itu, saya ingin komponen dirender hanya sekali dan saat itulah semua tindakan async selesai.

Saya mendapat nama dari beberapa middlewares yang tampaknya menangani masalah ini seperti redux-batched-subscribe dan redux-batched-actions . Tapi, sedikit bingung tentang pendekatan terbaik.

Jika ini adalah skenario yang valid, pendekatan apa yang akan disarankan untuk menangani ini?

@jintoppy jika permintaan Anda adalah untuk menghindari beberapa panggilan untuk membuat sebuah komponen maka solusi Promise.all tidak akan membantu Anda; karena doSomething() and doSomething() semua data pengiriman yang akan memicu perubahan status dan menyebabkan render komponen.

@jintoppy mengapa Anda memerlukan middlwares untuk menangani situasi Anda?
Apa yang menghalangi Anda melakukan sesuatu seperti itu.

// action creator
function fetchAB() {
  return dispatch => {
    const fetchA = fetch( 'api/endpoint/A' );
    const fetchB = fetch( 'api/endpoint/B' );
    return Promise.all([ fetchA, fetchB ])
      .then( values => dispatch(showABAction(values)) )
      .catch( err => throw err );
  }
} 

Anda dapat memasukkan semua permintaan Anda ke dalam satu pembuat tindakan dan mengirimkan tindakan hanya setelah semuanya diselesaikan. Dalam hal ini Anda memiliki peredam yang dipanggil dan dengan demikian status berubah hanya sekali, jadi render () kemungkinan besar akan diaktifkan hanya sekali juga.

@apranovich : Saya melihat satu masalah dengan pendekatan ini. Sekarang, saya perlu menggabungkan beberapa pengambilan dalam satu tindakan. Jadi, jika saya punya, katakanlah 10 panggilan asinkron, saya harus melakukan semua itu dalam satu tindakan. Selain itu, bagaimana jika saya ingin memicu satu dari 10 panggilan asinkron ini nanti? Dalam pendekatan ini, pada dasarnya, semua logika asinkron Anda akan berada dalam satu tindakan.

Terima kasih atas informasi yang berguna. Pertanyaan singkat, bagaimana hal yang sama bisa dicapai tetapi sebaliknya ketika pengambilan mengembalikan data yang diperlukan oleh metode pengambilan berikutnya. Misalnya, ada titik akhir API cuaca gratis fetch ) yang diperlukan untuk fetch untuk mendapatkan cuaca lokasi. Saya tahu ini adalah kasus penggunaan yang aneh, tetapi saya ingin tahu bagaimana menangani situasi serupa. Terima kasih sebelumnya!

Hai @gaearon , saya ingin mendapatkan ulasan Anda tentang pertanyaan saya ini. Saya harus membuat panggilan asinkron bersarang ke dua API. Respons dari API kedua bergantung pada respons yang pertama.

export const getApi=()=>{
        return(d)=>{
        axios({
            method:'GET',
            url:Constants.URLConst+"/UserProfile",
            headers:Constants.headers
        }).then((response)=>{
            return d({
                type:GET_API_DATA,
                response,
                axios({
                    method:'GET',
                    url:Constants.URLConst+"/UserProfileImage?enterpriseId="+response.data.ProfileData.EnterpriseId,
                    headers:Constants.headers
                }).then((response1)=>{
                    return d({
                        type:GET_PROFILE_IMAGE,
                        response1
                    })
                })
            })
        }).catch((e)=>{
            console.log("e",e);
        })
    }

}

Saya mencoba sesuatu seperti di atas, tetapi ini tidak berhasil. Dan dalam proyek saya, saya harus menggunakan respons dari kedua API secara independen.
Jadi, bagaimana cara yang benar untuk melakukannya?

Terima kasih.

@ c0b41 , silakan periksa pertanyaan yang diperbarui.

hey @ c0b41 , ini memberikan kesalahan sintaks pada panggilan axios kedua.

@ aayushis12 , @ c0b41 : ini adalah pelacak bug, bukan forum bantuan. Anda harus mencoba menanyakan pertanyaan ini di Stack Overflow atau Reactiflux. Akan ada lebih banyak orang yang melihat pertanyaan dan dapat membantu.

Bagi mereka yang mengalami itu

Saya melakukan segalanya

dicetak sebelum semua panggilan asinkron diselesaikan, periksa apakah Anda mengembalikan pernyataan atau ekspresi dari pembuat tindakan asinkron Anda.

Misalnya, jika doSomethingElse() dideklarasikan dengan {} dalam fungsi panah

function doSomethingElse() {
  return dispatch => {
    fetch(
      '/api/something'
    ).then(
      response => response.json()
    ).then(
      json => dispatch({ type: DO_SOMETHING_ELSE, json }),
      err => dispatch({ type: SOMETHING_ELSE_FAILED, err })
    );
  }
}

"Saya melakukan semuanya" akan dicetak sebelum doSomethingElse() selesai. Selesaikan ini dengan membuang {} setelah => .

@gaearon satu pertanyaan, berdasarkan https://github.com/reduxjs/redux/issues/723#issuecomment -139927639 bagaimana Anda akan menanganinya jika Anda perlu mengirimkan tindakan yang menunggu dua tindakan dikirim _AND_ status berubah karena tindakan itu? Saya mengalami kasus di mana tindakan dikirim tetapi tindakan di dalam then dijalankan _before_ status telah berubah. Kami menggunakan redux-thunk .

@enmanuelduran : Dan bukan lagi pengelola aktif - tolong jangan ping dia.

Sulit untuk menjawab pertanyaan Anda tanpa melihat kode, tetapi masalah kami sebenarnya tidak dimaksudkan untuk menjadi forum diskusi. Akan lebih baik jika Anda memposting pertanyaan Anda di Stack Overflow - Anda cenderung mendapatkan jawaban yang lebih baik di sana, lebih cepat.

@markerikson oh, maaf, itu bukan niat saya, membuat pertanyaan di stackoverflow jika seseorang tertarik: https://stackoverflow.com/questions/51846612/dispatch-an-action-after-other-specific-actions-were- dispatched-and-state-change

terima kasih banyak kawan.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat