Di mana seharusnya permintaan ajax dibuat di aplikasi Flux?

194

Saya membuat aplikasi react.js dengan arsitektur fluks dan saya mencoba mencari tahu di mana dan kapan permintaan untuk data dari server harus dibuat. Apakah ada contoh untuk ini? (Bukan aplikasi TODO!)

Eniz Gülek
sumber

Jawaban:

127

Saya seorang pendukung besar menempatkan operasi tulis async di pembuat tindakan dan operasi membaca async di toko. Tujuannya adalah untuk menjaga kode modifikasi status toko dalam penangan tindakan yang sepenuhnya sinkron; ini membuatnya mudah untuk alasan dan mudah untuk unit test. Untuk mencegah beberapa permintaan simultan ke titik akhir yang sama (misalnya, membaca dua kali), saya akan memindahkan pemrosesan permintaan aktual ke modul terpisah yang menggunakan janji untuk mencegah beberapa permintaan; sebagai contoh:

class MyResourceDAO {
  get(id) {
    if (!this.promises[id]) {
      this.promises[id] = new Promise((resolve, reject) => {
        // ajax handling here...
      });
    } 
    return this.promises[id];
  }
}

Walaupun bacaan di toko melibatkan fungsi asinkron, ada peringatan penting bahwa toko tidak memperbarui diri dalam penangan async, tetapi memecat suatu tindakan dan hanya memecat suatu tindakan ketika respons datang. Penangan untuk tindakan ini akhirnya melakukan modifikasi keadaan aktual.

Sebagai contoh, sebuah komponen mungkin melakukan:

getInitialState() {
  return { data: myStore.getSomeData(this.props.id) };
}

Toko akan menerapkan metode, mungkin, seperti ini:

class Store {
  getSomeData(id) {
    if (!this.cache[id]) {
      MyResurceDAO.get(id).then(this.updateFromServer);
      this.cache[id] = LOADING_TOKEN;
      // LOADING_TOKEN is a unique value of some kind
      // that the component can use to know that the
      // value is not yet available.
    }

    return this.cache[id];
  }

  updateFromServer(response) {
    fluxDispatcher.dispatch({
      type: "DATA_FROM_SERVER",
      payload: {id: response.id, data: response}
    });
  }

  // this handles the "DATA_FROM_SERVER" action
  handleDataFromServer(action) {
    this.cache[action.payload.id] = action.payload.data;
    this.emit("change"); // or whatever you do to re-render your app
  }
}
Michelle Tilley
sumber
Sudahkah Anda mencoba menaruh janji di dalam payload tindakan? Saya merasa lebih mudah untuk berurusan daripada mengirim beberapa tindakan
Sebastien Lorber
@SebastienLorber Imbas besar untuk fluks bagi saya adalah menjaga semua pembaruan negara di jalur kode sinkron, dan secara eksplisit hanya sebagai hasil dari tindakan pengiriman, jadi saya menghindari asinkron di dalam toko.
Michelle Tilley
1
@ Federico Masih belum jelas bagi saya apa solusi "terbaik" itu. Saya telah bereksperimen dengan strategi ini untuk memuat data yang dikombinasikan dengan menghitung jumlah permintaan async yang beredar. Sayangnya fluxdisuntikkan ke toko setelah konstruksi, jadi tidak ada cara yang bagus untuk mendapatkan tindakan dalam metode inisialisasi. Anda mungkin menemukan beberapa ide bagus dari lib fluks isomorofik Yahoo; ini adalah sesuatu yang harus didukung Fluxxor v2 dengan lebih baik. Jangan ragu untuk mengirim email kepada saya jika Anda ingin mengobrol lebih lanjut tentang ini.
Michelle Tilley
1
data: resultseharusnya data : data, kan? tidak ada result. mungkin lebih baik untuk mengubah nama param data menjadi payload atau sesuatu seperti itu.
oligofren
2
Saya menemukan utas lama ini sangat membantu - khususnya, komentar Bill Fisher dan Jing Chen. Ini sangat dekat dengan apa yang diusulkan oleh @BinaryMuse dengan perbedaan kecil bahwa pengiriman terjadi pada pembuat tindakan.
phillipwei
37

Fluxxor memiliki contoh komunikasi async dengan API.

Posting blog ini telah membicarakannya dan telah ditampilkan di blog React.


Saya menemukan ini pertanyaan yang sangat penting dan sulit yang belum dijawab dengan jelas, karena sinkronisasi perangkat lunak frontend dengan backend masih menyakitkan.

Haruskah permintaan API dibuat dalam komponen JSX? Toko? Tempat lain?

Melakukan permintaan di toko berarti bahwa jika 2 toko membutuhkan data yang sama untuk tindakan yang diberikan, mereka akan mengeluarkan 2 requets yang sama (kecuali jika Anda memperkenalkan dependensi antar toko, yang saya benar-benar tidak suka )

Dalam kasus saya, saya merasa ini sangat mudah untuk menempatkan janji Q sebagai payload tindakan karena:

  • Tindakan saya tidak perlu serializable (Saya tidak menyimpan log peristiwa, saya tidak perlu fitur replay acara dari sumber acara)
  • Ini menghilangkan kebutuhan untuk memiliki tindakan / peristiwa yang berbeda (permintaan dipecat / permintaan selesai / permintaan gagal) dan harus mencocokkannya menggunakan id korelasi ketika permintaan bersamaan dapat dipecat.
  • Hal ini memungkinkan beberapa toko untuk mendengarkan penyelesaian permintaan yang sama, tanpa memperkenalkan ketergantungan apa pun antara toko-toko (namun mungkin lebih baik untuk memperkenalkan lapisan caching?)

Ajax adalah Jahat

Saya pikir Ajax akan semakin jarang digunakan dalam waktu dekat karena sangat sulit untuk dipikirkan. Jalan yang benar? Mengingat perangkat sebagai bagian dari sistem terdistribusi, saya tidak tahu dari mana saya pertama kali menemukan ide ini (mungkin dalam video Chris Granger yang menginspirasi ini ).

Pikirkan tentang itu. Sekarang untuk skalabilitas, kami menggunakan sistem terdistribusi dengan konsistensi akhirnya sebagai mesin penyimpanan (karena kami tidak dapat mengalahkan teorema CAP dan seringkali kami ingin tersedia). Sistem ini tidak disinkronkan melalui polling satu sama lain (kecuali mungkin untuk operasi konsensus?) Melainkan menggunakan struktur seperti CRDT dan log peristiwa untuk membuat semua anggota sistem terdistribusi akhirnya konsisten (anggota akan konvergen ke data yang sama, diberikan waktu yang cukup) .

Sekarang pikirkan tentang apa itu perangkat seluler atau browser. Itu hanya anggota dari sistem terdistribusi yang mungkin menderita latensi jaringan dan partisi jaringan. (yaitu Anda menggunakan ponsel cerdas Anda di kereta bawah tanah)

Jika kita dapat membangun partisi jaringan dan database toleran kecepatan jaringan (maksud saya kita masih dapat melakukan operasi tulis ke node yang terisolasi), kita mungkin dapat membangun perangkat lunak frontend (ponsel atau desktop) yang terinspirasi oleh konsep-konsep ini, yang bekerja dengan baik dengan mode offline yang didukung oleh kotak tanpa fitur aplikasi tidak tersedianya.

Saya pikir kita harus benar-benar menginspirasi diri kita sendiri tentang bagaimana database bekerja untuk arsitektur aplikasi frontend kita. Satu hal yang perlu diperhatikan adalah bahwa aplikasi ini tidak melakukan permintaan POST dan PUT dan DAPATKAN ajax untuk saling mengirim data, melainkan menggunakan log peristiwa dan CRDT untuk memastikan konsistensi akhirnya.

Jadi mengapa tidak melakukannya di frontend? Perhatikan bahwa backend sudah bergerak ke arah itu, dengan alat seperti Kafka diadopsi secara besar-besaran oleh pemain besar. Ini entah bagaimana terkait dengan Event Sourcing / CQRS / DDD juga.

Lihat artikel-artikel keren dari penulis Kafka untuk meyakinkan diri sendiri:

Mungkin kita bisa mulai dengan mengirim perintah ke server, dan menerima aliran peristiwa server (melalui websockets untuk contoh), alih-alih menembakkan permintaan Ajax.

Saya tidak pernah merasa nyaman dengan permintaan Ajax. Saat kami Bereaksi para pengembang cenderung menjadi programmer fungsional. Saya pikir sulit untuk beralasan tentang data lokal yang seharusnya menjadi "sumber kebenaran" aplikasi frontend Anda, sedangkan sumber sebenarnya sebenarnya ada di database server, dan sumber kebenaran "lokal" Anda mungkin sudah usang. ketika Anda menerimanya, dan tidak akan pernah konvergen ke sumber nilai kebenaran yang sebenarnya kecuali Anda menekan beberapa tombol Refresh yang lemah ... Apakah ini rekayasa?

Namun masih agak sulit untuk merancang hal seperti itu karena beberapa alasan yang jelas:

  • Klien seluler / peramban Anda memiliki sumber daya yang terbatas dan tidak dapat selalu menyimpan semua data secara lokal (sehingga terkadang membutuhkan polling dengan konten berat permintaan ajax)
  • Klien Anda seharusnya tidak melihat semua data dari sistem terdistribusi sehingga entah bagaimana perlu menyaring peristiwa yang diterimanya untuk alasan keamanan
Sebastien Lorber
sumber
3
Bisakah Anda memberikan contoh menggunakan janji Q dengan tindakan?
Matt Foxx Duncan
@MattFoxxDuncan tidak yakin itu ide yang bagus karena membuat "event log" tidak dapat digunakan dan membuat toko memperbarui secara tidak sinkron pada aksi yang dipecat, jadi ia memiliki beberapa kelemahan. Namun jika itu ok untuk usecase Anda dan Anda memahami kelemahan ini cukup mudah dan mengurangi boilerplate. Dengan Fluxxor Anda mungkin dapat melakukan sesuatu sepertithis.dispatch("LOAD_DATA", {dataPromise: yourPromiseHere});
Sebastien Lorber
Sepenuhnya tidak setuju tentang argumen AJAX Anda. Bahkan itu sangat menjengkelkan untuk dibaca. Sudahkah Anda membaca komentar Anda? Pikirkan toko, game, aplikasi yang menghasilkan uang serius - semua membutuhkan panggilan server API dan AJAX .. lihat Firebase jika Anda ingin "tidak memiliki server" atau sesuatu yang bersifat seperti itu tetapi AJAX ada di sini untuk mengatakan saya harap setidaknya tidak ada orang lain yang setuju dengan logika Anda
TheBlackBenzKid
@TheBlackBenzKid Saya tidak mengatakan Ajax akan hilang sama sekali di tahun ini (dan pastikan saya masih membangun situs web di atas permintaan ajax yang saat ini sebagai CTO dari startup), tetapi saya mengatakan itu kemungkinan akan hilang karena itu bukan protokol yang cukup baik untuk menangani konsistensi akhirnya yang lebih membutuhkan streaming dan bukan polling, dan konsistensi akhirnya adalah apa yang memungkinkan untuk membuat aplikasi berfungsi offline secara andal (ya Anda dapat meretas sesuatu dengan penyimpanan lokal sendiri, tetapi Anda akan memiliki kapasitas offline yang terbatas, atau aplikasi Anda sangat sederhana). Masalahnya bukan caching, itu membuat cache itu tidak valid.
Sebastien Lorber
@TheBlackBenzKid Model di belakang Firebase, Meteor dll tidak cukup baik. Apakah Anda tahu bagaimana sistem ini menangani penulisan bersamaan? last-write-win bukan konsitensi kausal / strategi merger? Bisakah Anda membayar pekerjaan utama kolega Anda di aplikasi ketika keduanya bekerja pada koneksi yang tidak dapat diandalkan? Juga perhatikan bahwa sistem ini cenderung banyak pasangan modelisasi lokal dan server. Apakah Anda tahu aplikasi kolaboratif terkenal yang sangat kompleks, bekerja offline sempurna, menyatakan menjadi pengguna Firebase yang puas? Saya tidak
Sebastien Lorber
20

Anda dapat meminta data baik dari pembuat tindakan atau toko. Yang penting adalah untuk tidak menangani respons secara langsung, tetapi untuk membuat tindakan dalam panggilan balik kesalahan / sukses. Menangani respons langsung di toko mengarah ke desain yang lebih rapuh.

fisherwebdev
sumber
9
Bisakah Anda jelaskan hal ini secara lebih rinci? Katakanlah saya perlu melakukan pemuatan data awal dari server. Pada tampilan pengontrol, saya memulai INIT tindakan, dan Store memulai inisialisasi async yang mencerminkan tindakan ini. Sekarang, saya akan pergi dengan gagasan, bahwa ketika Toko mengambil data, itu hanya akan mengeluarkan perubahan, tetapi tidak memulai tindakan. Jadi, memancarkan perubahan setelah inisialisasi memberi tahu pandangan bahwa mereka bisa mendapatkan data dari toko. Mengapa ada kebutuhan tidak memancarkan perubahan pada pemuatan sukses, tapi mulai tindakan lain ?! Terima kasih
Jim-Y
Fisherwebdev, tentang toko yang meminta data, dengan melakukan itu, jangan Anda hancurkan paradigma Flux, satu-satunya 2 cara yang tepat yang dapat saya pikirkan untuk memanggil data adalah dengan menggunakan: 1. menggunakan kelas bootstrap menggunakan Tindakan untuk memuat data 2 Tampilan, sekali lagi menggunakan Tindakan untuk memuat data
Yotam
4
Memanggil data tidak sama dengan menerima data. @ Jim-Y: Anda hanya harus memancarkan perubahan begitu data di toko benar-benar berubah. Yotam: Tidak, meminta data di toko tidak merusak paradigma. Data hanya dapat diterima melalui tindakan, sehingga semua toko dapat diinformasikan oleh data baru yang memasuki aplikasi. Jadi kita dapat memanggil data di toko, tetapi ketika responsnya kembali, kita perlu membuat tindakan baru alih-alih menanganinya secara langsung. Ini membuat aplikasi fleksibel dan tangguh terhadap pengembangan fitur baru.
fisherwebdev
2

Saya telah menggunakan contoh Binary Muse dari contoh ajax Fluxxor . Ini adalah contoh sederhana saya menggunakan pendekatan yang sama.

Saya memiliki toko produk sederhana beberapa tindakan produk dan komponen tampilan-pengontrol yang memiliki sub-komponen yang semuanya menanggapi perubahan yang dibuat ke toko produk . Misalnya slider produk , daftar produk, dan komponen pencarian produk .

Klien Produk Palsu

Ini adalah klien palsu yang bisa Anda gantikan dengan menelepon produk pengembalian akhir aktual.

var ProductClient = {

  load: function(success, failure) {
    setTimeout(function() {
      var ITEMS = require('../data/product-data.js');
      success(ITEMS);
    }, 1000);
  }    
};

module.exports = ProductClient;

Toko Produk

Inilah Product Store, jelas ini adalah toko yang sangat minim.

var Fluxxor = require("fluxxor");

var store = Fluxxor.createStore({

  initialize: function(options) {

    this.productItems = [];

    this.bindActions(
      constants.LOAD_PRODUCTS_SUCCESS, this.onLoadSuccess,
      constants.LOAD_PRODUCTS_FAIL, this.onLoadFail
    );
  },

  onLoadSuccess: function(data) {    
    for(var i = 0; i < data.products.length; i++){
      this.productItems.push(data.products[i]);
    }    
    this.emit("change");
  },

  onLoadFail: function(error) {
    console.log(error);    
    this.emit("change");
  },    

  getState: function() {
    return {
      productItems: this.productItems
    };
  }
});

module.exports = store;

Sekarang tindakan produk, yang membuat permintaan AJAX dan setelah sukses memecat, tindakan LOAD_PRODUCTS_SUCCESS mengembalikan produk ke toko.

Tindakan Produk

var ProductClient = require("../fake-clients/product-client");

var actions = {

  loadProducts: function() {

    ProductClient.load(function(products) {
      this.dispatch(constants.LOAD_PRODUCTS_SUCCESS, {products: products});
    }.bind(this), function(error) {
      this.dispatch(constants.LOAD_PRODUCTS_FAIL, {error: error});
    }.bind(this));
  }    

};

module.exports = actions;

Memanggil begitu this.getFlux().actions.productActions.loadProducts() dari komponen apa pun yang mendengarkan toko ini akan memuat produk.

Anda dapat membayangkan memiliki tindakan yang berbeda yang akan menanggapi interaksi pengguna seperti addProduct(id) removeProduct(id)dll ... mengikuti pola yang sama.

Semoga contoh itu sedikit membantu, karena saya menemukan ini agak sulit untuk diterapkan, tetapi tentu saja membantu menjaga toko saya 100% sinkron.

svnm
sumber
2

Saya menjawab pertanyaan terkait di sini: Cara menangani panggilan api bersarang dalam fluks

Tindakan tidak seharusnya menjadi hal yang menyebabkan perubahan. Mereka seharusnya seperti surat kabar yang menginformasikan penerapan perubahan di dunia luar, dan kemudian aplikasi menanggapi berita itu. Toko-toko menyebabkan perubahan pada diri mereka sendiri. Tindakan hanya memberi tahu mereka.

Bill Fisher, pencipta Flux https://stackoverflow.com/a/26581808/4258088

Apa yang pada dasarnya harus Anda lakukan adalah, menyatakan melalui tindakan data apa yang Anda butuhkan. Jika toko mendapat informasi dari tindakan, ia harus memutuskan apakah perlu mengambil beberapa data.

Toko harus bertanggung jawab untuk mengumpulkan / mengambil semua data yang dibutuhkan. Penting untuk dicatat, bahwa setelah toko meminta data dan mendapat respons, itu harus memicu tindakan itu sendiri dengan data yang diambil, bertentangan dengan toko yang menangani / menyimpan respons secara langsung.

Toko bisa terlihat seperti ini:

class DataStore {
  constructor() {
    this.data = [];

    this.bindListeners({
      handleDataNeeded: Action.DATA_NEEDED,
      handleNewData: Action.NEW_DATA
    });
  }

  handleDataNeeded(id) {
    if(neededDataNotThereYet){
      api.data.fetch(id, (err, res) => {
        //Code
        if(success){
          Action.newData(payLoad);
        }
      }
    }
  }

  handleNewData(data) {
    //code that saves data and emit change
  }
}
MoeSattler
sumber
0

Inilah pendapat saya tentang ini: http://www.thedreaming.org/2015/03/14/react-ajax/

Semoga itu bisa membantu. :)

Jason Walton
sumber
8
downvote sesuai pedoman. menempatkan jawaban di situs eksternal membuat situs ini kurang bermanfaat, dan membuat untuk jawaban berkualitas lebih rendah, menurunkan kegunaan situs. url eksternal mungkin akan pecah waktunya juga. downvote tidak mengatakan apa-apa tentang kegunaan artikel, yang omong-omong sangat bagus :)
oligofren
2
Pos yang bagus, tetapi menambahkan ringkasan singkat pro / kontra dari setiap pendekatan akan membuat Anda mendapatkan suara positif. Pada SO, kita tidak perlu mengklik tautan untuk mendapatkan inti jawaban Anda.
Cory House