React: Terapkan Pemuatan Data Samping

Dibuat pada 13 Mar 2015  ·  136Komentar  ·  Sumber: facebook/react

Ini adalah API kelas satu untuk pemuatan data menyamping dari data stateless (walaupun berpotensi memo) dari penyimpanan/jaringan/sumber daya global, berpotensi menggunakan alat peraga/status sebagai input.

type RecordOfObservables = { [key:string]: Observable<mixed> };

class Foo {

  observe(): RecordOfObservables {
    return {
      myContent: xhr(this.props.url)
    };
  }

  render() {
    var myContent : ?string = this.data.myContent;
    return <div>{myContent}</div>;
  }

}

observasi() dijalankan setelah componentWillMount/componentWillUpdate tetapi sebelum render.

Untuk setiap kunci/nilai dalam catatan. Berlangganan ke Observable dalam nilai.

subscription = observable.subscribe({ onNext: handleNext });

Kami mengizinkan onNext untuk dipanggil secara sinkron dari berlangganan. Jika ya, kami menetapkan:

this.data[key] = nextValue;

Jika tidak, kami membiarkannya sebagai tidak terdefinisi untuk render awal. (Mungkin kita menyetelnya ke nol?)

Kemudian render berjalan seperti biasa.

Setiap kali onNext dipanggil, kami menjadwalkan "this.data[key]" baru yang secara efektif memicu pembaruan paksa pada komponen ini. Jika ini adalah satu-satunya perubahan, maka amati tidak dieksekusi ulang (componentWillUpdate -> render -> componentDidUpdate).

Jika props / state berubah (yaitu pembaruan dari recieveProps atau setState), maka observasi() dijalankan ulang (selama rekonsiliasi).

Pada titik ini kita mengulang record baru, dan berlangganan semua Observables baru.

Setelah itu, berhenti berlangganan Observables sebelumnya.

subscription.dispose();

Pengurutan ini penting karena memungkinkan penyedia data untuk melakukan penghitungan referensi cache mereka. Yaitu saya dapat menyimpan data selama tidak ada yang mendengarkannya. Jika saya segera berhenti berlangganan, maka jumlah referensi akan turun ke nol sebelum saya berlangganan data yang sama lagi.

Saat komponen dilepas, kami secara otomatis berhenti berlangganan dari semua langganan aktif.

Jika langganan baru tidak langsung memanggil Next, maka kami akan tetap menggunakan nilai sebelumnya.

Jadi jika this.props.url dari contoh saya berubah, dan saya berlangganan URL baru, myContent akan terus menampilkan konten url sebelumnya hingga url berikutnya terisi penuh.

Ini memiliki semantik yang sama dengan tag <img /> . Kami telah melihat bahwa, meskipun ini dapat membingungkan dan menyebabkan inkonsistensi, ini adalah default yang cukup waras, dan lebih mudah untuk membuatnya menunjukkan pemintal daripada memiliki default yang berlawanan.

Praktik terbaik mungkin dengan segera mengirim nilai "null" jika Anda tidak memiliki data yang di-cache. Alternatif lain adalah agar Observable memberikan URL (atau ID) dan konten dalam hasil.

class Foo {

  observe() {
    return {
      user: loadUser(this.props.userID)
    };
  }

  render() {
    if (this.data.user.id !== this.props.userID) {
      // Ensure that we never show inconsistent userID / user.name combinations.
      return <Spinner />;
    }
    return <div>Hello, {this.data.user.name} [{this.props.userID}]!</div>;
  }

}

Kita harus menggunakan kontrak RxJS dari Observable karena itu lebih umum digunakan dan memungkinkan eksekusi sinkron, tetapi begitu proposal @jhusain lebih umum digunakan, kita akan beralih ke kontrak itu.

var subscription = observable.subscribe({ onNext, onError, onCompleted });
subscription.dispose();

Kami dapat menambahkan lebih banyak kait siklus hidup yang merespons peristiwa ini jika perlu.

Catatan: Konsep ini memungkinkan data sideways untuk berperilaku seperti "perilaku" - seperti alat peraga. Ini berarti bahwa kita tidak perlu membebani keadaan gagasan untuk hal-hal ini. Ini memungkinkan pengoptimalan seperti membuang data hanya untuk berlangganan kembali nanti. Hal ini dapat dipulihkan.

Component API Big Picture

Komentar yang paling membantu

Jika ada yang ingin bermain-main dengan API semacam ini, saya membuat polyfill yang sangat bodoh untuk observe sebagai komponen urutan yang lebih tinggi:

import React, { Component } from 'react';

export default function polyfillObserve(ComposedComponent, observe) {
  const Enhancer = class extends Component {
    constructor(props, context) {
      super(props, context);

      this.subscriptions = {};
      this.state = { data: {} };

      this.resubscribe(props, context);
    }

    componentWillReceiveProps(props, context) {
      this.resubscribe(props, context);
    }

    componentWillUnmount() {
      this.unsubscribe();
    }

    resubscribe(props, context) {
      const newObservables = observe(props, context);
      const newSubscriptions = {};

      for (let key in newObservables) {
        newSubscriptions[key] = newObservables[key].subscribe({
          onNext: (value) => {
            this.state.data[key] = value;
            this.setState({ data: this.state.data });
          },
          onError: () => {},
          onCompleted: () => {}
        });
      }

      this.unsubscribe();
      this.subscriptions = newSubscriptions;
    }

    unsubscribe() {
      for (let key in this.subscriptions) {
        if (this.subscriptions.hasOwnProperty(key)) {
          this.subscriptions[key].dispose();
        }
      }

      this.subscriptions = {};
    }

    render() {
      return <ComposedComponent {...this.props} data={this.state.data} />;
    }
  };

  Enhancer.propTypes = ComposedComponent.propTypes;
  Enhancer.contextTypes = ComposedComponent.contextTypes;

  return Enhancer;
}

Penggunaan:

// can't put this on component but this is good enough for playing
function observe(props, context) {
  return {
    yourStuff: observeYourStuff(props)
  };
}

class YourComponent extends Component {
  render() {
    // Note: this.props.data, not this.data
    return <div>{this.props.data.yourStuff}</div>;
  }
}

export default polyfillObserve(YourComponent, observe);

Semua 136 komentar

undefined mungkin merupakan nilai teraman untuk ditetapkan ke data hingga observable memberikan nilai pertamanya melalui onNext . Sebagai contoh di Relay kami menetapkan arti yang berbeda untuk null (data tidak ada) dan undefined (belum diambil), jadi nilai data default ideal kami adalah undefined . Alternatifnya adalah menyediakan metode baru, misalnya getInitialData , tetapi saya menduga ini tidak perlu/berlebihan.

Ini cukup menarik namun dari sudut pandang pengetikan statis saya tidak begitu senang dengan sistem kunci/nilai, tipenya hampir tidak mungkin untuk diungkapkan.
Mengapa tidak memiliki observe mengembalikan satu yang dapat diamati dan mengatur/menggabungkan nilai yang diselesaikan menjadi this.data :

class Foo {

  observe() {
    return (
      loadUser(this.props.userID)
        .map(user => { user })
  }

  render() {
    if (this.data.user.id !== this.props.userID) {
      // Ensure that we never show inconsistent userID / user.name combinations.
      return <Spinner />;
    }
    return <div>Hello, {this.data.user.name} [{this.props.userID}]!</div>;
  }

}

Dan untuk kasus pengambilan banyak sesuatu seperti:

class Foo {

  observe() {
    return (
     combineLatest(
      loadUser(this.props.userID),
      loadSomethingElse(this.props.somethingElseId),
      (user, somethingElse) => ({ user, somethingElse})
     )
  }

  render() {
    ..
  }

}

Ini mungkin sedikit lebih bertele-tele, tetapi memungkinkan untuk memiliki tipe statis yang bagus :

interface Comp<T> {
  observe(): Observable<T>;
  data: T;
}

Juga alih-alih mengeksekusi ulang observe ketika props/state berubah, kita dapat memiliki akses ke 'props' 'state' sebagai observable :

class Foo {

  observe(propsStream) {
    return (
      propsStream
        .flatMap(({ userID }) => loadUser(userId))
        .map(user => { user })
    );
  }

  render() {
    if (this.data.user.id !== this.props.userID) {
      // Ensure that we never show inconsistent userID / user.name combinations.
      return <Spinner />;
    }
    return <div>Hello, {this.data.user.name} [{this.props.userID}]!</div>;
  }
}

Alasannya adalah karena kami tidak ingin mengharuskan penggunaan kombinator dan pemahaman RxJS untuk dapat berlangganan (beberapa) Observables. Menggabungkan dua Observables dengan cara ini cukup membingungkan. Faktanya, setidaknya untuk sumber data kami, kami mungkin akan mengimplementasikan API berlangganan tetapi bahkan tidak menyertakan kombinator pada prototipe Observables. Itu bukan persyaratan, tetapi Anda bebas menggunakan kombinator jika perlu.

Namun, untuk berlangganan toko Flux sederhana, Anda tidak perlu melakukannya.

Saya pikir Flow mungkin akan dapat menangani tipe statis ini menggunakan batasan, tetapi saya akan memeriksa dengan orang-orang itu untuk memastikan. Saya pikir itu akan cukup untuk mengetikkan properti data dan kemudian tipe observasi dapat diimplikasikan.

class Foo extends React.Component {
  data : { user : User, content : string };
  observe() /* implied as { user : Observable<User>, content : Observable<string> } */ {
  }
}

Perubahan ini bukan tentang masuk semua pada Observables sebagai cara untuk menggambarkan status aplikasi. Itu dapat diimplementasikan di atas ini, seperti yang telah Anda lakukan sebelumnya. Ini secara eksplisit bukan tentang status aplikasi karena metode ini idempoten. Kerangka kerja ini bebas untuk berhenti berlangganan dan berlangganan kembali sesuai kebutuhan.

cc @ericvicenti

Setidaknya dalam kasus TypeScript, tidak akan ada cara untuk membatasi tipe pengembalian observe berdasarkan tipe data , setidaknya sampai sesuatu seperti https://github.com/ Microsoft/TypeScript/issues/1295 diimplementasikan.

Saya ingin menggunakan ini untuk React DnD versi berikutnya, tetapi jelas ini membutuhkan menunggu React 0.14.
Saya ingin tahu apakah saya dapat "mengisi ulang" ini untuk saat ini dengan komponen tingkat tinggi yang menetapkan this.data pada instance ref .. Mungkin terlalu gila.

Apakah mungkin untuk menepati Janji? Kemudian seseorang dapat menggunakan pohon Janji untuk menyelesaikan data untuk seluruh pohon komponen sebelum render pertama! Ini akan sangat berguna untuk React sisi server.

Apa manfaat menjadikan ini sebagai API kelas satu? Ini pada dasarnya dapat dicapai dengan menggunakan "komponen tingkat tinggi".

Apa manfaat menjadikan ini sebagai API kelas satu? Ini pada dasarnya dapat dicapai dengan menggunakan "komponen tingkat tinggi".

Membungkus dalam 5 HOC untuk mendapatkan 5 langganan agak sulit dan sulit dipahami bagi pemula. Memahami componentWillReceiveProps juga tidak sepele. Ini memperbaiki keduanya.

Saya, misalnya, menyambut tuan-tuan baru kami yang dapat diamati.

Saya ingin tahu apakah ini dapat membantu membawa https://github.com/chenglou/react-state-stream lebih dekat ke Vanilla API React

Bukankah itu hanya membutuhkan satu HOC? Dalam contoh di pos Medium Anda, Anda mengulangi lebih dari stores dan berlangganan masing-masing.

stores.forEach(store =>
  store.addChangeListener(this.handleStoresChanged)
);

@aaronshaf Tergantung pada kasus penggunaan, pasti. Kadang-kadang berbeda jenis sumber negara, bukan hanya "beberapa toko". Tapi saya tidak bisa mengatakan atas nama tim React, mari kita dengar apa kata @sebmarkbage .

Akan senang semacam polyfill untuk bermain dengan ini sekarang. Saya belum mendapatkan ide itu sepenuhnya. Apa mekanisme yang terlibat dalam menangani pembaruan di masa mendatang. Saya akan meluangkan lebih banyak waktu untuk memahaminya. Saya pikir itu harus bisa dilakukan dengan mixin sederhana.

( @vjeux memberi tahu saya bahwa saya harus berpadu! jadi inilah saya.)

Saya tidak bermaksud untuk mempromosikan karya saya sendiri, tetapi menurut saya hook ini sangat mirip dengan hook getNexusBindings di React Nexus . Anda mendeklarasikan data deps pada level komponen melalui lifecycle hook (yang dapat bergantung pada props).

APInya terlihat seperti:

class UserDetails {
  getNexusBindings(props) {
    return {
      // binding to data in the datacenter
      posts: [this.getNexus().remote, `users/${this.props.userId}/posts`],
      // binding to data in the local flux
      mySession: [this.getNexus().local, `session`],
    }
  }
}

Pengikatan diterapkan/diperbarui selama componentDidMount dan componentWillReceiveProps . Dalam kasus terakhir, ikatan berikutnya berbeda dengan ikatan sebelumnya; binding yang dihapus akan dihentikan langganannya, binding yang ditambahkan akan dilanggankan. Mekanisme pengambilan/pembaruan yang mendasari dijelaskan dalam implementasi Nexus Flux . Pada dasarnya dengan API yang sama Anda dapat berlangganan data lokal (toko lokal tradisional) atau data jarak jauh (mengambil menggunakan GET dan menerima patch melalui Websockets/polyfill). Anda sebenarnya dapat berlangganan data dari jendela lain (menggunakan postWindow) atau WebWorker/ServiceWorker tetapi saya masih belum menemukan kasus penggunaan yang benar-benar berguna untuk ini.

Singkat cerita, Anda secara sinkron menjelaskan deps data di tingkat komponen menggunakan abstraksi Flux dan kait memastikan dependensi Anda secara otomatis berlangganan, disuntikkan pada pembaruan, dan berhenti berlangganan.

Tapi itu juga dilengkapi dengan fitur yang bagus: fungsi siklus hidup yang sama persis dimanfaatkan untuk melakukan pengambilan data pada waktu rendering sisi server. Pada dasarnya, mulai dari root dan secara rekursif dari sana, React Nexus mengambil terlebih dahulu binding, merender komponen, dan melanjutkan dengan turunan hingga semua komponen dirender.

@aaronshaf @gaearon Manfaat menjadikannya kelas satu adalah:

1) Itu tidak menggerogoti namespace props. Misalnya komponen tingkat tinggi tidak perlu mengklaim nama seperti data dari objek props Anda yang tidak dapat digunakan untuk hal lain. Merangkai beberapa komponen tingkat tinggi terus memakan lebih banyak nama dan sekarang Anda harus menemukan cara untuk membuat nama-nama itu tetap unik. Bagaimana jika Anda sedang menulis sesuatu yang mungkin sudah dibuat dan sekarang Anda memiliki konflik nama?

Selain itu, saya pikir praktik terbaik untuk komponen tingkat tinggi adalah menghindari perubahan kontrak komponen yang dibungkus. Yaitu secara konseptual itu harus menjadi alat peraga yang sama dengan yang keluar. Jika tidak, akan membingungkan untuk menggunakan dan men-debug ketika konsumen memasok seperangkat alat peraga yang sama sekali berbeda dari yang diterima.

2) Kita tidak perlu menggunakan state untuk menyimpan nilai terakhir. Konsep data mirip dengan props dalam arti bahwa itu murni memoisasi. Kita bebas membuangnya kapan saja jika kita perlu merebut kembali ingatannya. Misalnya, dalam gulir tak terbatas kita mungkin secara otomatis membersihkan subpohon yang tidak terlihat.

@RickWong Ya, akan cukup sepele untuk mendukung Promises karena mereka adalah bagian dari Observables. Kita mungkin harus melakukan itu agar tidak berpikiran. Namun, saya mungkin masih akan merekomendasikan untuk tidak menggunakannya. Saya menemukan bahwa mereka lebih rendah daripada Observables karena alasan berikut:

A) Mereka tidak dapat dibatalkan secara otomatis oleh kerangka kerja. Yang terbaik yang bisa kita lakukan adalah mengabaikan resolusi yang terlambat. Sementara itu, Janji memegang sumber daya yang berpotensi mahal. Sangat mudah untuk masuk ke situasi sulit berlangganan/membatalkan/berlangganan/membatalkan... dari timer/permintaan jaringan yang berjalan lama dan jika Anda menggunakan Promises, mereka tidak akan membatalkan di root dan oleh karena itu Anda hanya perlu menunggu sumber daya untuk menyelesaikan atau batas waktu. Ini dapat merusak kinerja di halaman desktop besar (seperti facebook.com) atau aplikasi kritis latensi di lingkungan dengan memori terbatas (seperti reaksi asli).

B) Anda mengunci diri hanya untuk mendapatkan satu nilai. Jika data itu berubah dari waktu ke waktu, Anda tidak dapat membatalkan tampilan Anda dan Anda berakhir dalam keadaan yang tidak konsisten. Ini tidak reaktif. Namun, untuk render sisi server tunggal yang mungkin baik-baik saja, pada klien Anda idealnya harus mendesainnya sedemikian rupa sehingga Anda dapat mengalirkan data baru ke UI dan secara otomatis memperbarui untuk menghindari data yang basi.

Oleh karena itu saya menemukan bahwa Observable adalah API yang unggul untuk dibangun karena tidak mengunci Anda untuk memperbaiki masalah ini jika perlu.

@elierotenberg Terima kasih telah

Dari sudut pandang server-rending, penting bagi kita untuk dapat menunda renderToString akhir hingga Observable/Promise diselesaikan dengan data yang dapat diambil secara asinkron. Jika tidak, kita masih dalam posisi harus melakukan semua pengambilan data asinkron di luar React tanpa mengetahui komponen mana yang akan ada di halaman.

Saya percaya react-nexus memungkinkan pemuatan asinkron terjadi di dalam komponen sebelum melanjutkan ke pohon render.

Ya, react-nexus secara eksplisit memisahkan:
1) deklarasi pengikatan sebagai getNexusBindings (yang merupakan metode siklus hidup bebas efek samping yang sinkron, mirip dengan render - sebenarnya dulunya adalah nama renderDependencies tapi saya pikir itu membingungkan),
2) mengikat langganan/pembaruan sebagai applyNexusBindings (yang sinkron dan membedakan ikatan nexus sebelumnya untuk menentukan ikatan baru mana yang harus berlangganan dan mana yang harus berhenti berlangganan)
3) mengikat prefetching sebagai prefetchNexusBindings (yang tidak sinkron dan diselesaikan ketika nilai "awal" (apa pun artinya ini) sudah siap)

ReactNexus.prefetchApp(ReactElement) mengembalikan Promise(String html, Object serializableData) . Hook ini meniru konstruksi pohon React (menggunakan instantiateReactComponent ) dan secara rekursif membangun/mengambil/merender komponen. Ketika seluruh pohon komponen 'siap', akhirnya memanggil React.renderToString , mengetahui bahwa semua data sudah siap (kesalahan modulo). Setelah diselesaikan, nilai Janji ini dapat disuntikkan dalam respons server. Di klien, siklus hidup React.render() biasa bekerja seperti biasa.

Jika ada yang ingin bermain-main dengan API semacam ini, saya membuat polyfill yang sangat bodoh untuk observe sebagai komponen urutan yang lebih tinggi:

import React, { Component } from 'react';

export default function polyfillObserve(ComposedComponent, observe) {
  const Enhancer = class extends Component {
    constructor(props, context) {
      super(props, context);

      this.subscriptions = {};
      this.state = { data: {} };

      this.resubscribe(props, context);
    }

    componentWillReceiveProps(props, context) {
      this.resubscribe(props, context);
    }

    componentWillUnmount() {
      this.unsubscribe();
    }

    resubscribe(props, context) {
      const newObservables = observe(props, context);
      const newSubscriptions = {};

      for (let key in newObservables) {
        newSubscriptions[key] = newObservables[key].subscribe({
          onNext: (value) => {
            this.state.data[key] = value;
            this.setState({ data: this.state.data });
          },
          onError: () => {},
          onCompleted: () => {}
        });
      }

      this.unsubscribe();
      this.subscriptions = newSubscriptions;
    }

    unsubscribe() {
      for (let key in this.subscriptions) {
        if (this.subscriptions.hasOwnProperty(key)) {
          this.subscriptions[key].dispose();
        }
      }

      this.subscriptions = {};
    }

    render() {
      return <ComposedComponent {...this.props} data={this.state.data} />;
    }
  };

  Enhancer.propTypes = ComposedComponent.propTypes;
  Enhancer.contextTypes = ComposedComponent.contextTypes;

  return Enhancer;
}

Penggunaan:

// can't put this on component but this is good enough for playing
function observe(props, context) {
  return {
    yourStuff: observeYourStuff(props)
  };
}

class YourComponent extends Component {
  render() {
    // Note: this.props.data, not this.data
    return <div>{this.props.data.yourStuff}</div>;
  }
}

export default polyfillObserve(YourComponent, observe);

Apakah Observable adalah hal yang konkret dan disepakati selain dari implementasi perpustakaan? Apa kontraknya, apakah cukup sederhana untuk diterapkan tanpa perlu menggunakan bacon atau Rxjs? Sebagus api kelas pertama untuk sideloading data, tampaknya aneh bagi React untuk menambahkan api berdasarkan primitif yang tidak ditentukan/sangat-initial-specing, mengingat gerakan stabil React menuju js biasa. Apakah hal seperti ini akan mengikat kita pada implementasi lahan pengguna tertentu?

sebagai tambahan mengapa tidak Streams? Saya tidak punya kuda dalam perlombaan, tapi jujur ​​saya bertanya-tanya; sudah ada pekerjaan yang dilakukan di aliran web, dan tentu saja ada simpul

@jquense Ada pekerjaan aktif pada proposal untuk menambahkan Observable ke ECMAScript 7(+) jadi idealnya ini akan menjadi JS biasa. https://github.com/jhusain/asyncgenerator (Saat ini kedaluwarsa.)

Kami tidak akan mengambil ketergantungan pada RxJS. API itu sepele untuk diimplementasikan sendiri tanpa menggunakan RxJS. RxJS adalah yang paling dekat dengan proposal ECMAScript aktif.

most.js tampaknya bisa dilakukan juga.

API Bacon.js tampaknya sulit digunakan tanpa ketergantungan pada Bacon karena penggunaan tipe Bacon.Event untuk memisahkan nilai.

Stream API terlalu tinggi dan jauh dari kasus penggunaan ini.

Apakah ada semacam opsi "menunggu sebelum render"? Maksud saya pada klien tidak perlu menunggu semua Observables sebelum rendering, tetapi pada server Anda ingin menunggu mereka untuk diselesaikan sehingga setiap komponen render() adalah penuh , tidak sebagian.

[parent] await observe(). full render(). -> [foreach child] await observe(). full render().

Dalam semua eksplorasi saya, saya menemukan bahwa ini adalah kait siklus hidup terpenting yang hilang di React sisi server.

Menindaklanjuti diskusi ini, saya mencoba meringkas apa yang dilakukan React Nexus di posting berikut:

Aplikasi Ismorphic dilakukan dengan benar dengan React Nexus

Inilah diagram dari rutinitas prefetching inti:

React Nexus

Kami tidak akan mengambil ketergantungan pada RxJS. API itu sepele untuk diimplementasikan sendiri tanpa menggunakan RxJS. RxJS adalah yang paling dekat dengan proposal ECMAScript aktif.

:+1: ini adalah masalah besar bagi saya, memikirkan tentang katakanlah, janji-janji di mana menerapkan milik Anda sendiri sangat penuh kecuali Anda tahu apa yang Anda lakukan. Saya pikir jika tidak, Anda berakhir dengan persyaratan implisit pada lib tertentu dalam ekosistem. Secara tangensial...salah satu hal yang menyenangkan dari dunia janji adalah rangkaian uji A+, jadi bahkan di seluruh perpustakaan setidaknya ada jaminan fungsi umum .then , yang membantu untuk interop janji sebelum mereka standar.

ini adalah perhatian besar bagi saya, berpikir tentang katakanlah, janji-janji di mana menerapkan Anda sendiri sangat penuh kecuali Anda tahu apa yang Anda lakukan. Saya pikir jika tidak, Anda berakhir dengan persyaratan implisit pada lib tertentu dalam ekosistem.

Senyawa. Untungnya observables memiliki kontrak yang sangat sederhana, dan bahkan tidak memiliki metode bawaan seperti then jadi dengan cara yang lebih sederhana daripada janji.

Mereka mungkin menjadi lebih rumit (dan lebih lambat) jika komite bersikeras bahwa pemanggilan next menjadwalkan tugas mikro seperti Promises.

Itu akan mengganggu banyak pola didasarkan pada fakta bahwa onNext sinkron di RxJS :/

Saya pikir pola penyimpanan Flux yang umum mungkin adalah menyimpan Peta yang Dapat Diamati berdasarkan per-kunci sehingga dapat digunakan kembali. Kemudian bersihkan ketika semua orang berhenti berlangganan.

Dengan begitu Anda dapat melakukan hal-hal seperti: MyStore.get(this.props.someID) dan selalu mendapatkan kembali Observable yang sama.

Dengan begitu Anda dapat melakukan hal-hal seperti: MyStore.get(this.props.someID) dan selalu mendapatkan kembali Observable yang sama.

Apakah menggunakan this.props.key (sudah saya tahu) masuk akal? Dalam kebanyakan kasus, Anda sudah melewati pengidentifikasi unik seperti itu dengan <... key={child.id} .. /> .

Dengan begitu Anda dapat melakukan hal-hal seperti: MyStore.get(this.props.someID) dan selalu mendapatkan kembali Observable yang sama.

Itulah pola yang saya gunakan untuk React Nexus juga. Store#observe mengembalikan pengamat yang telah di-memo dan tidak dapat diubah; itu dibersihkan (termasuk mekanisme pembersihan khusus backend yang relevan, seperti mengirim pesan "berhenti berlangganan" yang sebenarnya atau apa pun) ketika semua pelanggan pergi untuk setidaknya satu centang.

@sebmarkbage @gaearon Bagaimana cara mengamati pekerjaan di server di v0.14?
Apakah itu dapat menunggu dengan benar untuk semua pengamat menyelesaikan sebelum merender ke string yang mirip dengan bagaimana react-nexus (tetapi dibangun untuk bereaksi)?

IMO alangkah baiknya jika komponen menunggu nilai yang diamati pertama kali sebelum "siap" untuk dirender di server.

@gaearon : IMO alangkah baiknya jika komponen menunggu nilai yang diamati pertama kali sebelum "siap" untuk dirender di server.

Ya, :+1: untuk rendering asinkron. Sementara itu react-async oleh @andreypopp adalah alternatif, tetapi memerlukan fibers untuk "meretas" Bereaksi. Akan sangat bagus jika React dapat mendukung rendering asinkron di luar kotak.

Render asinkron adalah sesuatu yang ingin kami dukung tetapi bukan bagian dari masalah ini. L

Mungkin tidak akan berhasil di 0,14 sayangnya. Banyak desain berbeda untuk dipertimbangkan dan diperlukan refactor.

Jangan ragu untuk membuat dan menerbitkan deskripsi perubahan arsitektur pada internal yang diperlukan untuk mewujudkannya.

Saya memiliki pemikiran yang sama dengan @gaearon re: react-streaming-state . Mengingat semua aplikasi potensial selain pemuatan samping, mungkinkah ada nama yang lebih baik daripada data ? Misalnya, observed akan lebih jelas mengaitkannya dengan metode.

Tidak bermaksud menggelincirkan dengan bikeshedding tetapi ingin membuang ini di luar sana.

tidak bisa menunggu untuk diamati di Bereaksi. ini seharusnya membuat React reaktif seperti yang saya pahami

Saya bereksperimen dengan ide serupa saat menulis ulang react-async , lihat README .

Perbedaan yang mencolok adalah bahwa saya memperkenalkan identitas eksplisit yang dapat diamati/proses untuk merekonsiliasi proses, mirip dengan bagaimana React melakukannya dengan key prop dan komponen stateful.

Ketika id dari proses bernama berubah, React Async menghentikan instance proses lama dan memulai yang baru.

APInya terlihat seperti:

import React from 'react';
import Async from 'react-async';

function defineFetchProcess(url) {
  return {
    id: url,
    start() {
      return fetch(url)
    }
  }
}

function MyComponentProcesses(props) {
  return {
    user: defineFetchProcess(`/api/user?user${props.userID}`)
  }
}

@Async(MyComponentProcesses)
class MyComponent extends React.Component {

  render() {
    let {user} = this.props
    ...
  }
}

API proses sekarang mengikuti ES6 Promises API secara sintaksis dan nama, tetapi secara semantik tidak diharapkan process.then(onNext, onError) dipanggil hanya sekali per proses langsung. Itu dibuat untuk mengakomodasi kasus penggunaan (?) paling populer dalam mengambil data melalui janji. Tapi sejujurnya, sekarang saya pikir saya perlu mengubahnya untuk mencegah kebingungan.

Saya mungkin salah, tetapi saya pikir satu-satunya hal yang mencegah penerapan API yang diusulkan (dalam masalah ini) di userland adalah kurangnya kait siklus hidup yang dieksekusi tepat sebelum render seperti componentWillUpdate tetapi dengan props dan state sudah diinstal pada instance.

Satu hal yang belum dibahas adalah penanganan callback onError . Jika observable menghasilkan kesalahan, informasi itu harus tersedia untuk komponen entah bagaimana. Karena React menangani panggilan subscribe(callbacks) yang sebenarnya, kita memerlukan metode standar untuk menginjeksi ke objek panggilan balik itu.

Untuk memberikan fleksibilitas paling besar bagi pengembang aplikasi, saya melihat dua pendekatan. Yang pertama adalah menempatkan kesalahan pada atribut tingkat atas, mirip dengan this.data . Ini tampaknya sangat berat, dan selanjutnya memakan namespace komponen.
Yang kedua akan memungkinkan pengembang untuk mendefinisikan panggilan balik onError mereka sendiri sebagai fungsi siklus hidup. Jika saya ingin memiliki penanganan kesalahan khusus untuk pengamatan saya, saya dapat menambahkan sesuatu seperti

onObserveError(key, error) {
  // do something with the error
  this.state.errors[key] = error;
  this.setState({ errors: this.state.errors });
}

Ini mirip dengan apa yang telah kami lakukan pada iterasi Parse+React yang akan datang. Kami membuat penanganan kesalahan kami sendiri untuk menghasilkan API yang kami inginkan. Kesalahan ditambahkan ke peta { name => error } pribadi, dan komponen memiliki metode publik tingkat atas, queryErrors() , yang mengembalikan tiruan peta jika tidak kosong, dan null jika tidak (memungkinkan if (this.queryErrors()) sederhana.

Tentu saja, mendefinisikan metode cadangan baru juga merupakan bisnis yang rumit; Saya tidak akan berpura-pura tidak. Tetapi kita membutuhkan cara untuk secara implisit atau eksplisit membuat kesalahan tersedia untuk komponen saat merender.

@andrewimm Idenya adalah untuk memasukkannya ke dalam sistem propagasi kesalahan umum yang meningkatkan kesalahan hierarki hingga ditangani oleh batas kesalahan. https://github.com/facebook/react/issues/2928

Ini juga harus menangani kesalahan dalam metode melempar dan memulihkan dengan anggun. Seperti jika metode render() melempar. Ini secara signifikan lebih banyak pekerjaan dan akan membutuhkan waktu untuk diterapkan dengan benar tetapi idenya adalah untuk menyatukan penanganan kesalahan dengan cara ini.

Saya berpendapat ini harus dibiarkan di luar reaksi yang tepat, dan 1 atau 2 titik integrasi utama harus dikoordinasikan dengan proyek-proyek seperti react-async dan react-nexus sehingga ini dapat dilakukan dengan bersih di atas React yang tepat....

Saya setuju, sepertinya memiliki cara yang disarankan untuk melakukan ini lebih baik daripada
memanggang ini ke dalam kerangka itu sendiri

Pada Selasa, 21 Apr 2015, 23:38 Rodolfo [email protected]
menulis:

Saya berpendapat ini harus dibiarkan di luar reaksi yang tepat, dan 1 atau 2 kunci
titik integrasi harus dikoordinasikan dengan proyek seperti react-async dan
react-nexus sehingga ini dapat dilakukan dengan bersih di atas React yang tepat ....


Balas email ini secara langsung atau lihat di GitHub
https://github.com/facebook/react/issues/3398#issuecomment -95048028.

Selama akhir pekan, saya membuat implementasi Flux lain, yang disebut Flexy . Dalam hal ini, periksa kode untuk toko. Ini memperlihatkan metode .getObservable yang sesuai dengan API yang dapat diamati, meskipun sebenarnya tidak memiliki yang dapat diamati atau kerangka kerja Reaktif lainnya yang digunakan.

Jadi, saya akan mengatakan bahwa API cukup mudah untuk dibuat dengan Observables yang sebenarnya.

Yang mengatakan, jangan menilai kode dengan kasar, itu dilakukan selama akhir pekan untuk:

  • seru
  • pemahaman
  • menggunakan js-csp
  • menggunakan API yang diamati

Catatan tambahan, sistem seperti ini dan Flux sebenarnya membuat rendering sisi server tidak terlalu menyakitkan. Menggunakan sistem yang mirip dengan React-Nexus, kita dapat menginisialisasi penyimpanan dan meneruskannya ke aplikasi React. Kami kemudian dapat memantau penyimpanan dan petugas operator dan terus melakukan rendering ulang hingga tidak ada lagi tindakan yang dilakukan (semua data yang diperlukan sudah ada di penyimpanan).

Saya berpendapat bahwa ini adalah titik integrasi terkecil untuk mendapatkan semantik baru dari langganan data tanpa kewarganegaraan. Apa poin integrasi lain yang akan Anda sarankan? Tidak termasuk rendering async yang merupakan masalah yang jauh lebih kompleks dan layak mendapatkan utasnya sendiri, dan kait ini dapat digunakan dengan rendering async apa pun.

Segala sesuatu yang lain sudah mungkin untuk diterapkan di atas React pada basis per-komponen. Perhatikan bahwa kita tidak akan melakukan penyuntikan plugin secara global karena hal itu merusak penggunaan kembali komponen di seluruh lingkungan sehingga kerangka kerja yang dibangun di atas React harus kontekstual untuk masing-masing komponen.

Kait apa yang kita lewatkan?

Hai,

Sejujurnya, sementara kait baru akan membuat implementasi lebih mudah, kami tentu saja dapat mencapai pemuatan data menyamping tanpa mereka, seperti yang telah kami tunjukkan dengan react-async dan react-nexus .

Jika ada, mengekspos dan mendukung pemeliharaan siklus hidup instance komponen React di luar hierarki React yang terpasang akan membantu. Di react-nexus Saya menggunakan internal instanciateReactComponent dan memanggil componentWillMount , componentWillUnmount , dll, sendiri, dan saya menganggap pendekatan ini rapuh (bagaimana jika instanciateReactComponents bergantung pada invarian internal yang berubah pada versi React berikutnya?).

Adapun pendekatan stateless, menurut saya data async mengambil _is_ stateful, dan dengan demikian menyimpan status tertunda/selesai/gagal dalam status beberapa komponen adalah relevan. Saat kami menggunakan react-nexus di aplikasi dunia nyata, kami memiliki komponen tingkat tinggi yang melakukan pengambilan data dan memasukkan status pengambilannya ke komponen turunannya sebagai props. Dengan demikian, komponen dalam adalah "stateless" (yang diinginkan), dan komponen luar adalah "stateful" (yang juga diinginkan, misalnya untuk menampilkan pemintal pemuatan atau placeholder).

Apa poin integrasi lain yang akan Anda sarankan?

menurut saya @ANDREYPOPP mengajukan pertanyaan yang tepat. bukankah satu-satunya hal yang kita perlukan untuk mengimplementasikan ini di userland adalah lifecycle hook sebelum render? itu sepertinya perubahan API minimal terkecil yang diperlukan, sisanya adalah mengatur dan memicu forceUpdate dengan tepat saat Anda mengubah data berdasarkan aliran input/emitor/observable apa pun. Kecuali saya kehilangan sesuatu yang istimewa tentang itu (sepenuhnya mungkin)?

Saya tidak tertarik pada diskusi yang lebih besar di sini.
Tetapi untuk menjawab @sebmarkbage, saya pikir salah satu kait terpenting yang saya inginkan adalah sesuatu yang hanya diperlukan saat tidak menggunakan observables nyata.

  • Kait yang dapat menyediakan fungsi yang dapat menangani nilai yang didorong oleh _sebelum_ yang dapat diamati yang disetel ke data. Dengan pengamatan nyata, ini hanya akan menjadi .map

Jika situasinya sedikit lebih terbuka, saya pikir harus ada kait untuk menggantikan perilaku khusus yang dapat diamati dengan kait khusus. Dengan cara ini kita bisa menggunakan event-emitter, atau saluran CSP sebagai gantinya.

Sepertinya saya seperti beberapa komentar terakhir mengatakan bahwa titik ekstensi terkecil sebenarnya adalah kait siklus hidup "hubungkan" dan "putuskan" - yang membuatnya mudah untuk melampirkan data async ke komponen dan mengelola langganan itu?

Observables kemudian dapat dibangun dengan cukup sepele di atasnya (di dalam atau di luar inti) - tetapi mengekspos poin-poin siklus hidup itu memiliki daya tarik yang lebih luas?

Apakah itu ringkasan yang masuk akal?

Saya juga belum yakin bahwa ini perlu diselesaikan di React itu sendiri. Saya lebih cenderung berpikir bahwa React harus menyediakan kait yang diperlukan untuk membangun fungsionalitas ini di atas React.

Tapi untuk sesaat, katakanlah kita akan menggunakan observe() . Beberapa pemikiran:

Kami memiliki this.props , this.state , this.context dan sekarang this.data , semuanya sebagai sumber potensial data baru di render() . Ini tampak berlebihan bagi saya. Apakah ide untuk memisahkan status aplikasi dari status komponen? Ini mungkin memperjelas dan memisahkan beberapa masalah di sekitar negara bagian, tetapi saya merasa biaya untuk memperkenalkan input baru mungkin tidak lebih besar daripada keuntungannya. Jika kita ingin this.state difokuskan hanya pada status komponen, mengapa tidak membiarkan bidang di this.data ditambahkan ke this.props atau this.context saja?

Nama this.data terlalu umum. Alat peraga adalah data, status adalah data, dan variabel lokal apa pun adalah data. Nama itu tidak menambah arti dan mengacaukan arti yang ada. Saya lebih suka this.observed atau nama lain yang sebenarnya berarti sesuatu. Jadi beri +1 pada komentar @matthewwithanm :

mungkin ada nama yang lebih baik dari data ? Misalnya, observed akan lebih jelas mengaitkannya dengan metode.

Jika kita membiarkan observe() dieksekusi di server, kita memerlukan semacam pengait yang akan membersihkan kebocoran memori yang mungkin disebabkan, karena pelepasan tidak akan pernah terjadi.

Jika kita memanggil observe() lagi setiap kali props atau state berubah (dan context ?) maka observe harus dioptimalkan untuk kinerja dan idealnya tidak bisa dibuat mahal secara tidak sengaja. Itu menjadi bagian dari jalur panas. Saya suka ini dari React Nexus:

binding berikutnya berbeda dengan binding sebelumnya; binding yang dihapus akan dihentikan langganannya, binding yang ditambahkan akan dilanggankan.

Saya menjadi percaya bahwa keadaan memperumit komponen dan saya telah mencoba untuk menggunakannya lebih sedikit di komponen saya sendiri dan mengangkatnya sebagai gantinya. Inilah sebabnya, selain kekhawatiran yang dikemukakan @fisherwebdev (yang saya setujui sekarang), saya tidak yakin bahwa membiarkan observe bergantung pada state adalah ide yang bagus. State bergantung pada props, diamati bergantung pada state _and_ props.. Bukankah terlalu rumit? Saya lebih suka memiliki observe(props) .

Saya juga menyadari bahwa amati tidak boleh bergantung pada state , sebagian besar karena tidak perlu. Seperti yang ditunjukkan @gaearon , cukup mudah untuk menaikkan status ke satu tingkat di atas, yang terasa lebih bersih setelah memisahkan masalah. Ketika observe berpotensi bergantung pada state , logika seputar penanganan pembaruan dalam komponen menjadi jauh lebih kompleks; ketika itu hanya bergantung pada props , Anda dapat memiliki penangan tanpa garpu di componentDidMount / componentWillReceiveProps . Lebih sedikit kode di jalur kritis menghasilkan siklus rendering yang lebih sederhana, dan juga mengurangi jumlah pembaruan yang tidak diinginkan yang memicu ulang langganan.

+1 untuk mengamati (alat peraga)

Semakin sedikit kita berurusan dengan negara semakin baik IMO.

Saya berpendapat, bahwa sebagai perpustakaan, React harus berusaha sefleksibel mungkin. Saya setuju bahwa itu bukan ide yang baik untuk mengamati bergantung pada negara. Ya itu bisa rumit. Ya, praktik terbaik seharusnya tidak bergantung pada negara.
Tapi itu adalah pilihan yang harus dilakukan oleh pengguna React.
Saya akan merekomendasikan, membiarkan API saat ini tidak berubah (selain dari kemungkinan kait untuk menambahkan lebih banyak fleksibilitas, untuk membuatnya bekerja dengan lebih dari sekadar yang dapat diamati misalnya), dan membuktikan dokumentasi yang menjelaskan bahwa menggunakan status dalam metode observasi tidak disarankan.

Saya percaya bahwa setiap orang ingin melakukan hal yang benar, dan bahwa kami ingin diarahkan ke arah yang benar. Mengamati status tidak menerima membuatnya lebih mudah bagi pengguna daripada secara tidak sengaja menemukan sesuatu seperti "ini adalah antipattern" di dokumen.

EDIT: maaf, baru sadar saya sedang mencari flatMap . Saya bingung sendiri karena saya pikir itu akan meratakan susunan pesan, tetapi itu beroperasi pada tingkat yang lebih tinggi (pesan dapat diamati).

Apakah API yang diusulkan akan menangani kasus di mana hasil dari satu bidang data bergantung pada hasil yang lain? Yaitu Anda memetakan hasil pertama dan mengembalikan yang dapat diamati:

observe(props, context) {
  if (!props.params.threadID) {
    return {};
  }

  const observeThread = ThreadStore.observeGetByID(
    {id: props.params.threadID}
  );
  return {
    thread: observeThread,
    messages: observeThread.map(thread => {
      return MessageStore.observeGetByIDs({ids: thread.messageIDs});
    })
  };
}

Saya cukup baru dalam hal yang dapat diamati secara umum, jadi saya mungkin melakukan ini sepenuhnya salah. Di tanah janji ini sangat sederhana, karena mengembalikan janji dari then akan menyebabkan then didasarkan pada janji itu.

Saya tidak mengerti komentar yang seharusnya tidak bergantung pada this.state . Keadaan yang dienkapsulasi pasti membuat React jauh lebih rumit tetapi hanya itu saja. Jika tidak ada status yang dienkapsulasi, kita hanya memerlukan pustaka mode langsung yang di memo. Jika Anda masuk semua ke "Toko", maka ya, Anda tidak perlu status tetapi bukan itu yang ditentukan oleh React untuk Anda lakukan.

Kami memiliki beberapa pola yang mengharuskan Anda membuat pembungkus tambahan tetapi untuk penggunaan normal Anda tidak perlu melakukannya. Selain penyimpanan, data yang Anda amati selalu bergantung pada status meskipun secara tidak langsung. Saya tidak berpikir itu adalah praktik yang buruk sama sekali untuk bergantung padanya dalam mengamati. Misalnya

observe() {
  return { items: Items.getPagedItems({ pageIndex: this.state.currentPage }) };
}

Bahkan jika observe tidak bergantung pada state itu masih akan bergantung pada props dan context . Bahkan jika itu tidak bergantung pada context Anda masih memiliki tipuan yang menggunakan context untuk merender props komponen yang menggunakannya di observe .

observe perlu dievaluasi ulang setiap kali render pass turun karena props bisa saja berubah. Namun, kami pasti akan membedakan hasil observasi dan tidak berhenti berlangganan/berlangganan kembali jika observasi yang sama dikembalikan. Namun, kami tidak dapat melakukan diff pada properti individual (selain dengan shouldComponentUpdate) jadi idealnya Anda harus mengimplementasikan cache Anda sendiri menggunakan Map sebagai fitur daya. Dengan begitu Anda dapat mengembalikan observable yang sama ke beberapa komponen di pohon. Misalnya beberapa komponen memuat pengguna yang sama. Bahkan jika tidak, Anda hanya membuat ulang Observable dan akhirnya mencapai cache paling bawah. Beginilah cara React rekonsiliasi bekerja. Ini tidak terlalu lambat.

Hook observe ini tidak dirancang untuk membuat React sepenuhnya reaktif dalam arti bahwa status ditangkap di Observables. Saat ini tujuan desain utama adalah untuk menghindari keadaan terperangkap dalam penutupan dan kombinator dan sebagai gantinya memiliki pohon keadaan bersih terpisah yang bagus yang dapat dibekukan dan dihidupkan kembali, dan berpotensi dibagikan ke seluruh pekerja.

Yang membawa saya ke titik akhir saya ...

Kami tentu saja tidak _perlu_ menambahkan ini ke perpustakaan inti. Antarmuka "publik" asli adalah mountComponent/receiveComponent dan Anda dapat membangun seluruh sistem komponen komposit Anda di atasnya. Namun, tidak banyak orang yang menggunakannya jauh lebih kuat untuk menaikkan bilah abstraksi karena sekarang kita dapat membangun hal-hal lain yang diaktifkan oleh bilah abstraksi yang lebih tinggi. Seperti optimasi seluruh komponen.

Tujuan utama React adalah untuk membuat kontrak antar komponen sehingga abstraksi yang berbeda dalam ekosistem dapat hidup berdampingan. Bagian penting dari peran itu adalah meningkatkan tingkat abstraksi untuk konsep umum sehingga kami dapat mengaktifkan fitur lintas komponen baru. Misalnya menyimpan semua status subpohon, lalu menghidupkan kembali subpohon tersebut. Atau mungkin termasuk unmount otomatis di server atau mengubah aspek waktu rekonsiliasi di server.

Penting juga untuk menyediakan beberapa baterai yang disertakan untuk membuat semua ini enak dan seragam.

Penting untuk disadari bahwa mikro-modularisasi (seperti menambahkan kait siklus hidup baru) tidak sepenuhnya merupakan kemenangan murni untuk membangunnya ke dalam kerangka kerja. Ini juga berarti bahwa tidak mungkin lagi untuk bernalar tentang abstraksi sistem yang luas.

Saya berharap sesuatu seperti ini ada di dokumen sebagai "tujuan filosofi / desain / non-tujuan".

Tujuan utama React adalah untuk membuat kontrak antar komponen sehingga abstraksi yang berbeda dalam ekosistem dapat hidup berdampingan. Bagian penting dari peran itu adalah meningkatkan tingkat abstraksi untuk konsep umum sehingga kami dapat mengaktifkan fitur lintas komponen baru.

Suka ini. Saya setuju dengan @gaearon bahwa

Kami tentu saja tidak perlu menambahkan ini ke pustaka inti.... Namun, tidak banyak orang yang menggunakannya jauh lebih kuat untuk menaikkan bilah abstraksi karena kami sekarang dapat membangun hal-hal lain yang diaktifkan oleh bilah abstraksi yang lebih tinggi. Seperti optimasi seluruh komponen.

Saya merasa keengganan (setidaknya bagi saya) bukan dalam menambahkan API lain, tetapi menambahkan satu yang bergantung pada konstruksi non-bahasa (masih didefinisikan) agar berfungsi. Ini benar-benar dapat bekerja dengan baik, tetapi saya khawatir tentang masalah yang dihadapi perpustakaan Promise di mana tidak ada perpustakaan janji (bahkan yang keluhan spesifikasi) dapat saling percaya, yang mengarah pada pembungkusan yang tidak perlu dan pekerjaan defensif untuk memastikan mereka menyelesaikan dengan benar, yang membatasi peluang pengoptimalan . Atau lebih buruk lagi Anda terjebak seperti jQuery dengan implementasi rusak yang tidak pernah bisa berubah.

@jquense Saya sepenuhnya setuju. Saya ingin menambahkan kait ini sejak lama. (Eksperimen asli: https://github.com/reactjs/react-page/commit/082a049d2a13b14199a13394dfb1cb8362c0768a )

Keraguan dua tahun lalu adalah masih terlalu jauh dari standarisasi. Saya menginginkan protokol standar sebelum kami menambahkannya juga inti.

Saya pikir kita sampai pada titik di mana banyak kerangka kerja setuju tentang perlunya sesuatu seperti yang Dapat Diamati dan standarisasi mencapai titik di mana API yang cocok telah diusulkan. Saya yakin kita harus sedikit mengubahnya, tetapi selama arsitektur tingkat tinggi berfungsi, itu harus dapat ditukar dan akhirnya menyatu.

Saya pikir apa yang terjadi dengan Promises adalah bahwa API dan cerita debugging sangat kurang di area tertentu yang tidak dialami oleh Observables. Ini adalah cerita yang lebih lengkap di luar kotak di mana Janji harus menstandarisasi solusi minimal yang tidak lengkap.

Satu-satunya perbedaan pendapat tentang: Observables yang saya amati (tidak bisa menolak, maaf) adalah potensi Zalgo. Apakah Observables dapat mendorong nilai secara sinkron dalam menanggapi langganan. Beberapa orang tampaknya menentangnya, tetapi penggunaan Observables oleh React akan bergantung pada ini sejauh yang saya mengerti. Bisakah Anda mengomentari itu?

Secara umum saya tidak menemukan Zalgo menjadi masalah dengan Observables karena konsumen selalu memegang kendali dan dapat memilih untuk selalu-async dengan sesuatu seperti observeOn .

Senang bahwa akhirnya ada beberapa konsensus tentang ini. Saya pribadi lebih suka saluran daripada Observables, tetapi jika Observables akan ditambahkan ke bahasa, saya setuju tidak perlu menunggu lebih lama lagi.

Karena itu, mari kita pastikan bahwa kita menjaga API cukup terbuka untuk bekerja dengan non-Observable yang sesuai dengan API dasar.

Saya juga tidak menemukan Zalgo menjadi masalah. Namun dengan Observables Anda dapat menggunakan penjadwal untuk memastikan asinkron jika diinginkan, penjadwal default sekarang asinkron sehingga Anda dapat menggunakannya sesuai kebutuhan.

@sebmarkbage Saya pikir Anda telah mengatasi sebagian besar masalah saya dan sekarang saya melihat manfaat dari menambahkan ini ke kerangka kerja. Namun, dapatkah Anda mengomentari this.data -- (1) dapatkah/haruskah kita melipat bidang tersebut menjadi props/konteks/negara atau (2) dapatkah kita mengganti namanya?

Sedikit bikeshed-y tetapi tetap melakukannya ... masalah Zalgo sebenarnya bukan tentang apakah itu penting dalam hal harapan API, ini adalah salah satu interop yang dapat diamati dan kemudahan implementasi. Tidak memiliki kesepakatan awal tentang Zalgo yang telah menempatkan dunia perpustakaan Promise pada posisi yang menyebalkan karena harus bersikap super defensif ketika berhadapan dengan Promise dari lib lain. (poin saya di atas diulang di bawah)

... di mana tidak ada perpustakaan janji (bahkan yang keluhan spesifikasi) dapat saling percaya, yang mengarah pada pembungkusan yang tidak perlu dan pekerjaan defensif untuk memastikan mereka menyelesaikan dengan benar

Karena janji-janji awal tidak semuanya sesuai dengan resolusi async, kami sekarang berada dalam posisi di mana bahkan perpustakaan yang ditentukan tidak dapat menganggap thenables layak dipercaya, membunuh potensi optimasi. Ini menurut saya sangat relevan di sini, di mana React tidak akan menyediakan implementasi yang Dapat Diobservasi untuk digunakan (siapa yang menginginkannya?), dan kami kemungkinan besar bertahun-tahun lagi untuk dapat hanya mengandalkan browser yang disediakan Observable, jadi perpustakaan yang mudah interop itu penting. Plus ke poin @gaearon , jika React bergantung pada panggilan sinkronisasi dan ditentukan untuk selalu async yang menempatkan kita dalam posisi seperti jquery terjebak dengan implementasi jahat.

Saya sangat setuju. Saya ingin menambahkan kait ini sejak lama. Keraguan dua tahun lalu adalah masih terlalu jauh dari standarisasi. Saya menginginkan protokol standar sebelum kami menambahkannya juga inti.

Saya senang itu dihadiri juga dan dipikirkan, itu pasti menghibur. :) dan secara umum saya pikir adopsi awal janji sepadan dengan kontra yang saya diskusikan di sini, jadi jangan anggap kekhawatiran saya sebagai tidak suka, atau tidak setuju, saya cukup senang dengan prospek API kelas satu untuk ini dan saya juga melihat bagaimana Observable benar-benar merupakan pilihan yang baik/paling masuk akal di sini untuk itu.

"Kita harus menggunakan kontrak RxJS dari Observable karena itu lebih umum digunakan dan memungkinkan eksekusi sinkron, tetapi begitu proposal @jhusain lebih umum digunakan, kita akan beralih ke kontrak itu sebagai gantinya."

Hanya untuk menambahkan sedikit lebih banyak konteks. Ada inisiatif Reactive Streams (http://www.reactive-streams.org/) untuk menyediakan standar pemrosesan aliran asinkron dengan tekanan balik non-blocking. Ini mencakup upaya yang ditujukan untuk lingkungan runtime (JVM dan JavaScript) serta protokol jaringan.

Implementasi terkemuka saat ini adalah Fe Akka Streams atau RxJava. Saya tidak tahu apakah RxJs sudah mematuhi antarmuka yang sama sekarang, antarmuka saat ini untuk Pelangganadalah onSubscribe(Langganan s), onNext(T t), onCompleted(), onError(Throwable t).

Bisakah Anda menjelaskan lebih lanjut tentang apa proposal @jhusain ?

Saya tidak tahu apakah React harus benar-benar mematuhi inisiatif ini karena jika saya perlu, saya mungkin dapat menempatkan RxJs (dengan asumsi itu akan sesuai) di antara dan beradaptasi dengan antarmuka React dan membiarkan konsep yang lebih maju seperti tekanan balik ke RxJs (meskipun saya akan melakukannya lebih suka tidak harus banyak beradaptasi).

Apakah ada posisi atau tujuan terkait inisiatif ini?

@vladap Saya percaya ini adalah proposal yang disebutkan dari @jhusain

Saya telah membaca @jhusain dan saya tidak yakin tentang motivasi untuk pindah ke spesifikasi ini di masa depan. Apakah ada keuntungan khusus?

Spesifikasi aliran-reaktif memiliki dukungan yang lebih besar dan sudah ada di versi 1.0 . Karena RxJava sudah mengimplementasikan spesifikasi ini, saya berasumsi RxJs akan mengikuti (tetapi belum memeriksa).

Blog ini merangkum antarmuka dengan beberapa contoh menggunakan aliran Akka.

Saya dapat melihat beberapa keuntungan memiliki antarmuka yang sama di backend dan frontend, terutama karena saya bekerja pada keduanya. Mungkin membantu untuk bekerja sama antara grup backend dan frontend tetapi di sisi lain saya berasumsi bahwa websocket atau sse adalah titik integrasi aktual untuk streaming.

Saya tidak dapat menemukan daftar pelaksana di www.reactive-streams.org sekarang, tetapi terakhir kali saya memeriksanya adalah:

Björn Antonsson – Typesafe Inc.
Gavin Bierman – Oracle Inc.
Jon Brisbin – Perangkat Lunak Penting Inc.
George Campbell – Netflix, Inc
Ben Christensen – Netflix, Inc
Mathias Doenitz – spray.io
Marius Eriksen – Twitter Inc.
Tim Fox – Red Hat Inc.
Viktor Klang – Typesafe Inc.
Roland Kuhn – Typesafe Inc.
Doug Lea – SUNY Oswego
Stephane Maldini – Pivotal Software Inc.
Norman Maurer – Red Hat Inc.
Erik Meijer – Dualitas Terapan Inc.
Todd Montgomery – Kaazing Corp.
Patrik Nordwall – Typesafe Inc.
Johannes Rudolph – semprotan.io
Endre Varga – Typesafe Inc.

Mungkin saya melangkah terlalu jauh di sini tetapi saya percaya bahwa konteks yang lebih besar dapat membantu dalam keputusan di masa depan.

@vladap Dari apa yang saya mengerti dan apa yang saya lihat pada masalah github @jhusain sudah bekerja dengan mereka, jadi saya kira kita tidak akan memiliki banyak masalah.
Dari perspektif antarmuka, dari apa yang saya juga dapat pahami dalam berbagai masalah github dan dokumen spesifikasi lainnya, pengamat pasti akan menghormati antarmuka generator sehingga sesuatu seperti:

{
  next(value),
  throw(e),
  return(v)
}

Cukup mengimplementasikan pengamatan yang sangat mendasar dengan metode 'berlangganan' tunggal yang menghormati antarmuka itu harus aman untuk bereaksi.

Sepertinya hanya penamaan yang berbeda dengan fungsi yang sama, dalam arti itu baik-baik saja. Saya mungkin lebih suka penamaan yang sama seperti pada spesifikasi tetapi pada akhirnya saya tidak terlalu peduli sejauh metode ini melakukan hal yang sama.

Saya tidak dapat mengevaluasi setara yang hilang dengan onSubscribe(). Di blog saya telah menyebutkan penulis mengatakan bahwa itu adalah kunci untuk mengontrol tekanan balik. Saya tidak tahu itu memiliki kasus penggunaan lain. Dari sini saya berasumsi bahwa React tidak peduli dengan pengendalian tekanan balik atau ada strategi lain untuk itu. Ini adalah hal yang kompleks maka saya mengerti itu bukan perhatian React.

Apakah saya mengerti dengan benar bahwa strateginya adalah sesuatu yang sejalan - baik aplikasinya rumit dan dapat timbul masalah tekanan balik kemudian gunakan sesuatu di antaranya untuk menyelesaikannya, seperti RxJS, atau Anda menghubungkan komponen Bereaksi langsung ke soket web fe dan Anda tidak 'tidak memiliki masalah tekanan balik karena aplikasinya sederhana dan memiliki pembaruan yang lambat.

Di mana saya dapat menemukan antarmuka yang dapat diamati yang diusulkan untuk ECMAScript di masa mendatang? Jika sudah ada.

Proposal saat ini dapat ditemukan di sini:

https://github.com/jhusain/asyncgenerator

JH

Pada 7 Mei 2015, pukul 2:32 pagi, vladap [email protected] menulis:

Di mana saya dapat menemukan antarmuka yang dapat diamati yang diusulkan untuk ECMAScript di masa mendatang? Jika sudah ada.


Balas email ini secara langsung atau lihat di GitHub.

Proposal Aliran Reaktif (RSP) lebih jauh dari proposal TC-39 karena memperkenalkan Observable yang menangani tekanan balik. RSP Observable dioptimalkan untuk mengirim aliran secara efisien ke seluruh jaringan, dengan tetap memperhatikan tekanan balik. Ini sebagian didasarkan pada pekerjaan yang dilakukan di RxJava, yang merupakan bagian rekayasa yang sangat mengesankan (pengungkapan penuh: dirancang oleh rekan di Netflix, Ben Christensen).

Alasan utama keputusan untuk membakukan tipe Observable yang lebih primitif adalah kehati-hatian. Observable yang lebih primitif adalah kontrak ganda dari ES2015 Iterable, yang memberi kami jaminan berharga bahwa tipenya setidaknya sefleksibel tipe yang sudah distandarisasi di ES2015. Selain itu, ada berbagai macam kasus penggunaan di JS untuk Observable yang tidak memerlukan tekanan balik. Di browser, DOM adalah wastafel paling umum untuk aliran push, dan bertindak secara efektif seperti buffer. Mengingat bahwa tipe RSP lebih kompleks, pendekatan kami adalah menstandarkan tipe yang lebih primitif terlebih dahulu, dan kemudian meninggalkan ruang untuk mengimplementasikan tipe yang lebih maju nanti. Idealnya kami akan menunggu sampai divalidasi di user-land.

FYI RxJS saat ini tidak memiliki rencana untuk mengimplementasikan RSP Observable.

JH

Pada 7 Mei 2015, pukul 02:30, vladap [email protected] menulis:

Sepertinya hanya penamaan yang berbeda dengan fungsi yang sama, dalam arti itu baik-baik saja. Saya mungkin lebih suka penamaan yang sama seperti pada spesifikasi tetapi pada akhirnya saya tidak terlalu peduli sejauh metode ini melakukan hal yang sama.

Saya tidak dapat mengevaluasi setara yang hilang dengan onSubscribe(). Di blog saya telah menyebutkan penulis mengatakan bahwa itu adalah kunci untuk mengontrol tekanan balik. Saya tidak tahu itu memiliki kasus penggunaan lain. Dari sini saya berasumsi bahwa React tidak peduli dengan pengendalian tekanan balik atau ada strategi lain untuk itu. Ini adalah hal yang kompleks maka saya mengerti itu bukan perhatian React.

Apakah saya mengerti dengan benar bahwa strateginya adalah sesuatu yang sejalan - baik aplikasinya rumit dan dapat timbul masalah tekanan balik kemudian gunakan sesuatu di antaranya untuk menyelesaikannya, seperti RxJS, atau Anda menghubungkan komponen Bereaksi langsung ke soket web fe dan Anda tidak 'tidak memiliki masalah tekanan balik karena aplikasinya sederhana dan memiliki pembaruan yang lambat.


Balas email ini secara langsung atau lihat di GitHub.

Terima kasih banyak atas detail berharganya. Ini sangat masuk akal.

@gaearon saya agak menyalin Anda. Saya ingin menggunakan ParseReact dengan kelas ES6 jadi saya perlu mengimplementasikan ulang api observasi mixin sebagai komponen tingkat tinggi.

https://Gist.github.com/amccloud/d60aa92797b932f72649 (penggunaan di bawah)

  • Saya mengizinkan observasi untuk didefinisikan pada komponen atau diteruskan ke dekorator. (Saya mungkin menggabungkan keduanya)
  • Saya menggabungkan observables sebagai props (tidak this.data, atau this.props.data)

@aaronshaf @gaearon Manfaat menjadikannya kelas satu adalah:

1) Itu tidak menggerogoti namespace props. Misalnya komponen tingkat tinggi tidak perlu mengklaim nama seperti data dari objek props Anda yang tidak dapat digunakan untuk hal lain. Merangkai beberapa komponen tingkat tinggi terus memakan lebih banyak nama dan sekarang Anda harus menemukan cara untuk membuat nama-nama itu tetap unik. Bagaimana jika Anda sedang menulis sesuatu yang mungkin sudah dibuat dan sekarang Anda memiliki konflik nama?

Selain itu, saya pikir praktik terbaik untuk komponen tingkat tinggi adalah menghindari perubahan kontrak komponen yang dibungkus. Yaitu secara konseptual itu harus menjadi alat peraga yang sama dengan yang keluar. Jika tidak, akan membingungkan untuk menggunakan dan men-debug ketika konsumen memasok seperangkat alat peraga yang sama sekali berbeda dari yang diterima.

Alih-alih HOC, itu bisa menjadi komponen biasa:

import Observe from 'react/addons/Observe';

class Foo {
  render() {
    return (
      <Observe
        render={this.renderData}
        resources={{
          myContent: xhr(this.props.url)
        }} />
    );
  }

  renderData({ myContent }) {
    if (myContent === null) return <div>Loading...</div>;
    return <div>{myContent}</div>;
  }
}

Karena Anda mengontrol fungsi yang diteruskan sebagai prop render , tidak mungkin nama-nama tersebut bertabrakan. Di sisi lain, tidak mencemari negara pemilik.

Apa yang kulewatkan di sini?

Ini bahkan bisa kurang bertele-tele jika komponen Observe baru saja mengambil Observables dari props-nya:

render() {
  return (
    <Observe myContent={Observable.fetch(this.props.url)}
             render={this.renderData} />
  );
}

renderData({ myContent }) {
  if (myContent === null) return <div>Loading...</div>;
  return <div>{myContent}</div>;
}

Yang juga bagus tentang ini adalah akan menghindari langganan ulang jika shouldComponentUpdate mengembalikan false karena kita tidak akan masuk ke render sama sekali.

Terakhir, seseorang dapat menulis dekorator untuk render yang membungkusnya menjadi komponen Observe :

@observe(function (props, state, context) {
  myContent: Observable.fetch(props.url)
})
render({ myContent }) {
  if (myContent === null) return <div>Loading...</div>;
  return <div>{myContent}</div>;
}

Saya lebih suka menyimpan render sebagai fungsi rendering murni daripada menyuntikkan logika pengambilan data di dalamnya.
Proposal awal sepertinya bagus menurut saya. Ini sangat dekat dengan cara kerja negara dengan rx-react dan itu akan memungkinkan untuk memisahkan manajemen negara dari logika pengambilan data yang tampaknya sangat koheren.

Satu-satunya hal yang mengganggu saya adalah penggunaan peta yang dapat diamati alih-alih satu yang dapat diamati, karena itu tidak memberi pengguna kemungkinan untuk memilih bagaimana yang dapat diamati disusun tetapi ini adalah masalah kecil.

Itu tidak benar-benar menyuntikkan logika pengambilan data, hanya menyimpan beberapa pengetikan. Ini akan sesuai dengan versi di atas, yang hanya merender komponen <Observe /> . Bukan hal yang aneh untuk merender komponen stateful, jadi menurut saya itu tidak membuat render kurang murni daripada sekarang.

Saya mencoba menggabungkan yang terbaik dari #3858 dan proposal ini.

Dalam pendekatan HOC apa pun, manfaatnya adalah eksplisit, tetapi kerugiannya dijelaskan oleh @sebmarkbage : nama alat peraga mungkin bertentangan di beberapa titik.

Dalam proposal saat ini, manfaatnya adalah eksplisit, tetapi sisi negatifnya adalah siklus hidup yang lebih rumit dan permukaan API komponen inti yang lebih besar.

Di #3858, manfaatnya adalah menempatkan dependensi "memoized render" dengan rendering itu sendiri (mereka tidak digunakan di tempat lain sehingga masuk akal), tetapi saya khawatir tentang "terlihat sinkron tetapi asinkron" , dan tidak memahami caranya dapat bekerja dengan model yang tidak dapat diubah jika sangat bergantung pada this . Itu juga membuat saya salah paham dengan cara React-was-easy-to-reason-about karena tidak ada yang mudah tentang penalaran tentang pelacakan perubahan secara manual dan menggabungkan sumber data ke React (atau membungkusnya untuk bekerja dengan React). Saya mengerti ada tekanan untuk mengimplementasikan sesuatu yang berkinerja baik dan mengurangi boilerplate.

Dalam proposal saya, saya menjaga colocation dan eksplisit, tetapi:

  • <Observe /> (atau observe() dekorator yang membungkus render dengan <Observe /> ) hanyalah sebuah add-on, saya tidak mengusulkan _any_ perubahan ke inti React.
  • Setiap orang dapat menerapkan logika pengamatan mereka sendiri untuk kasus penggunaan mereka. Anda dapat memiliki <Observe /> Anda sendiri yang hanya menggunakan satu yang dapat diamati, jika itu yang Anda inginkan. Anda mungkin atau mungkin tidak menggunakan dekorator.
  • Siklus hidup komponen tetap sama.
  • Tidak ada konflik prop karena data dilewatkan sebagai parameter.
  • Kami melakukan dogfood dengan memecahkan masalah dengan alat yang kami miliki.
  • Untuk mengurangi boilerplate, kami menggunakan alat reduksi boilerplate (dekorator) daripada memperkenalkan konsep inti baru.

Diskusi hebat dan bekerja di sekitar sini, sangat hormat. :)

Saya setuju bahwa ini tidak pantas membuatnya menjadi inti. Mungkin sebuah add-on memberi proposal ini daya tarik yang cukup sehingga orang dapat mencoba untuk menyatukan dan menstandarkannya sebelum melakukan lebih sepenuhnya. Meskipun demikian, saya menemukan proposal ini lebih baik daripada https://github.com/facebook/react/issues/3858 dan https://github.com/facebook/react/pull/3920 untuk minimalisnya.

Ini adalah sesuatu yang saya gunakan di proyek sampingan (jadi butiran garam) - ini mirip dengan karya luar biasa @elierotenberg tetapi tidak mengambil alih siklus hidup, karena aplikasi ini tidak 100% dalam Bereaksi dan harus interop.

Persiapkan diri Anda untuk CoffeeScript dan mixin, atau terus menyipitkan mata hingga ini terlihat seperti ES6 jika Anda mau. :)

_ = require 'lodash'

module.exports = DeclareNeedsMixin = 
  componentDidMount: ->
    <strong i="12">@needsConsumerId</strong> = _.uniqueId @constructor.displayName
    <strong i="13">@sinkNeeds</strong> <strong i="14">@props</strong>, <strong i="15">@state</strong>

  componentWillUpdate: (nextProps, nextState) ->
    <strong i="16">@sinkNeeds</strong> nextProps, nextState

  componentWillUnmount: ->
    @props.flux.declareNeeds <strong i="17">@needsConsumerId</strong>, []

  sinkNeeds: (props, state) ->
    if not @declareNeeds?
      return console.warn 'Missing method required for DeclareNeedsMixin: `declareNeeds`', @

    needs = <strong i="18">@declareNeeds</strong> props, state
    props.flux.declareNeeds <strong i="19">@needsConsumerId</strong>, needs

  # Intended to be overridden by the host class.
  # Returns a set of facts, stored as an array.
  # Yes, immutable data is awesome, that's not the point here though. :)
  # Facts are serializable data, just values.
  # declareNeeds: (props, state) ->
  #   []

Dan digunakan seperti ini:

module.exports = EmailThreads = React.createClass
  displayName: 'EmailThreads'
  mixins: [DeclareNeedsMixin]

  propTypes:
    flux: PropTypes.flux.isRequired

  declareNeeds: (props, state) ->
    [Needs.GmailData.myThreads({ messages: 20 })]

  ...

Jadi declareNeeds adalah fungsi satu arah dari props dan menyatakan deskripsi tentang apa yang dibutuhkan komponen ini. Dalam implementasi aktual di sini, bagian penerima @props.flux.declareNeeds , yang diatur pada komponen tingkat atas, menenggelamkan kebutuhan ini ke dalam objek ProcessSink . Itu mengelompokkan sesukanya, menghapus penipuan needs di seluruh komponen yang berbagi flux yang sama, dan kemudian melakukan efek samping untuk memenuhi kebutuhan tersebut (seperti menghubungkan ke soket atau membuat permintaan HTTP). Ini menggunakan penghitungan referensi untuk membersihkan hal-hal stateful seperti koneksi soket ketika tidak ada komponen yang membutuhkannya lagi.

Data mengalir dari bit stateful seperti peristiwa soket dan permintaan ke operator (lalu ke Toko atau di mana pun) dan kembali ke komponen untuk memenuhi kebutuhan mereka. Ini tidak sinkron sehingga semua komponen menangani rendering saat data belum tersedia.

Saya membagikan ini di sini hanya sebagai suara lain yang mengeksplorasi solusi semacam ini dimungkinkan di ruang pengguna, dan bahwa API saat ini melayani eksperimen semacam itu dengan sangat baik. Dalam hal langkah minimal yang dapat dilakukan inti untuk mendukung interop antara pendekatan yang berbeda, saya pikir @elierotenberg berhasil:

Jika ada, mengekspos dan mendukung pemeliharaan siklus hidup instance komponen React di luar hierarki React yang terpasang akan membantu.

Adapun pendekatan stateless, menurut saya pengambilan data async adalah stateful, dan dengan demikian menyimpan status tertunda/selesai/gagal dalam status beberapa komponen adalah relevan.

@elierotenberg dan @andrewimm membuat poin bagus tentang penanganan kesalahan kelas satu. @sebmarkbage Saya pikir insting Anda terhadap titik interop minimal benar, tapi saya tidak yakin bagaimana menambahkan semantik untuk kesalahan untuk menggelembungkan pohon komponen sesuai dengan persyaratan itu. Perlu ada cerita yang sama sederhananya di sini untuk cara mengakses nilai dari onError dan onCompleted , meskipun hanya slot di this.observed memegang nilai terakhir dari next/error/completed suka { next: "foo" } . Tanpa mendukung kontrak yang dapat diamati sebagai fitur kelas satu, saya agak skeptis tentang proposal ini yang berhasil.

Dan karena ini adalah internet, dan salah satu kali pertama saya berbicara di sini: umpan React Issues adalah beberapa bacaan terbaik dan sumber serba guna untuk karya dan ide hebat. :+1:

baunya seperti ikatan bagi saya.

Saya tidak yakin bagaimana ini terkait dengan proposal/implementasi saat ini, tetapi saya menemukan bahwa mengaktifkan komposisi dan manipulasi tingkat tinggi sebenarnya adalah fitur utama pelacakan ketergantungan data, terutama jika Anda menggunakan sumber data reaktif (fluks, fluks, melalui kabel, atau apa pun selain memberi Anda pembaruan).

Ambil contoh berikut dengan react-nexus@^3.4.0 :

// the result from this query...
@component({
  users: ['remote://users', {}]
})
// is injected here...
@component(({ users }) =>
  users.mapEntries(([userId, user]) =>
    [`user:${userId}`, [`remote://users/${userId}/profile`, {}]]
  ).toObject()
))
class Users extends React.Component {
  // ... this component will receive all the users,
  // and their updates.
}

Secara keseluruhan, saya bertanya-tanya apakah harus ada pengikatan data di API komponen sama sekali. Tampak bagi saya bahwa dekorator yang mengembalikan komponen tingkat tinggi menyediakan cara yang sangat bagus untuk mengekspresikan pengikatan data tanpa mencemari namespace metode komponen.

Namun, seperti yang dicatat oleh @sebmarkbage , ada risiko pencemaran namespace props sebagai gantinya. Untuk saat ini, saya menggunakan dekorator transformator props ( react-transform-props ) untuk membersihkan/mengganti nama props sebelum meneruskannya ke komponen bagian dalam, tetapi saya mengakui itu mungkin menjadi masalah di masa mendatang jika komponen-komponen yang lebih tinggi menjadi lebih umum dan risiko bentrokan nama meningkat.
Bisakah ini diselesaikan dengan menggunakan simbol adalah kunci properti? Apakah propTypes memeriksa mendukung Symbol -keyed props? Akankah JSX mendukung kunci alat peraga inline yang dihitung (walaupun seseorang selalu dapat menggunakan properti yang dihitung + penyebaran objek)?

Maaf jika ini sedikit keluar dari topik, tetapi bagi saya tampaknya kita masih harus menemukan abstraksi/API manis yang tepat untuk mengekspresikan deps data di tingkat komponen.

2 saya

Saya telah bersenang-senang dengan pola 'anak-anak sebagai fungsi' untuk nilai-nilai yang berubah seiring waktu. Saya percaya @elierotenberg pertama kali muncul? Penggunaan saya saat ini adalah untuk memodelkan pegas (melalui rebound) pada pegas reaksi. Contoh -

<Springs to={{x: 20, y: 30}} tension={30}>
  {val => <div style={{left: val.x, top: val.y}}>moving pictures</div>}
</Springs>

Tidak ada tabrakan alat peraga, dan tidak ada penggunaan status pemilik. Saya juga dapat membuat sarang beberapa pegas, dan bereaksi mengelola semua bit yang sulit. Ini 'dapat diamati' (ha!) Juga dapat menerima onError , onComplete dan alat peraga lainnya (permintaan graphql?).

Upaya kasar untuk membuat sketsa ini untuk 'fluks'

<Store 
  initial={0}
  reduce={(state, action) => action.type === 'click'? state+1 : state} 
  action={{/* assume this comes as a prop from a 'Dispatcher' up somewhere */}}> 
    {state => <div onClick={() => dispatch({type: 'click'})}> clicked {state} times</div>}
</Store>

Kami menggunakan pola ini, yang kami sebut _render callbacks_, di Asana tetapi pada akhirnya menjauh darinya.

kelebihan

  • Tidak ada potensi tabrakan alat peraga.
  • Mudah diimplementasikan baik sebagai penulis StoreComponent dan pengguna StoreComponent

Kontra

  • shouldComponentUpdate sangat sulit untuk diterapkan karena panggilan balik render dapat menutup status. Bayangkan kita memiliki pohon komponen A -> Store -> B . A memiliki penghitung dalam statusnya yang diakses selama panggilan balik render sebagai props untuk B . Jika A diperbarui karena penghitung dan Store tidak diperbarui, B akan memiliki versi penghitung yang basi. Alhasil, kami terpaksa selalu update Store.
  • Pengujian komponen secara terpisah menjadi sangat sulit. Mau tidak mau, pengembang menempatkan logika kompleks dalam panggilan balik render untuk komponen mana yang akan dirender dan ingin menguji logikanya. Cara alami untuk melakukannya adalah dengan merender seluruh pohon dan menguji _melalui_ komponen toko. Hal ini membuat tidak mungkin untuk menggunakan perender dangkal.

Kami sekarang pindah ke model di mana StoreComponent mengambil ReactElement dan ketika toko menerima data baru, toko mengkloning ReactElement menimpa prop tertentu. Ada banyak cara untuk melakukan pola ini seperti mengambil konstruktor Component dan props. Kami menggunakan pendekatan ini karena ini adalah model yang paling mudah dalam TypeScript.

Poin yang hebat, tidak dapat memikirkan jalan keluar tanpa melanggar persyaratan 'menyamping'.

@threepointone Kedengarannya persis seperti salah satu proposal implementasi untuk https://github.com/reactjs/react-future/pull/28

@pspeter3 dalam contoh Anda, apakah ada kelemahan serius untuk membuat Toko selalu memperbarui / shouldComponentUpdate: ()=> true ? Saya pikir 'anak-anaknya' akan dirender tanpa Store dalam rantai. Terima kasih atas waktunya!

@threepointone Itulah tepatnya yang kami lakukan untuk batasan kami. Tidak jelas apa dampaknya, tetapi ada kekhawatiran dari anggota tim. Kekhawatiran yang dikombinasikan dengan kesulitan pengujian memaksa peralihan untuk menggunakan React.cloneElement(this.props.child, {data: this.state.data})

@pspeter3 sudut pengujian jelas merupakan masalah. Bagaimana jika dangkalRenderer mengenali 'panggilan balik render'? Apakah itu membantu?

Ps- 'render callback' :thumbs_up:

@sebmarkbage diskusi saat ini tentang es-observable adalah bahwa subscribe akan dijamin asinkron, dengan metode [Symbol.observer] disediakan untuk pintasan dan berlangganan secara sinkron, meskipun validitas memiliki kedua api sinkronisasi/async adalah saat ini sedang diperdebatkan.

Saya telah menimpali tiket ini dengan kasus penggunaan yang disebutkan di atas untuk mendukung langganan sinkron, tidak tahu apakah Anda mungkin memiliki sesuatu untuk ditambahkan di sana.

Saya pikir ide-ide di sini adalah pola yang sangat bersih untuk menangani keadaan eksternal, meskipun setelah bermain-main sebentar, saya condong ke pendekatan HOC.

Juga @gaearon Saya menyederhanakan bit HOC Anda di atas, menggunakan static - ComposedComponent.observe dan menggunakan this.state daripada this.state.data - https://Gist.github.com/tgriesser/d5d80ade6f895c28e659

Ini terlihat sangat bagus dengan dekorator es7 yang diusulkan:

<strong i="20">@observing</strong>
class Foo extends Component {
  static observe(props, context) {
    return {
      myContent: xhr(props.url)
    };
  }
  render() {
    var myContent = this.props.data.myContent;
    return <div>{myContent}</div>;
  }
}

Dekorator kelas bahkan dapat menambahkan pengambil untuk data untuk membuatnya sangat dekat dengan API yang diusulkan asli (dikurangi keadaan lokal yang memengaruhi langganan yang dapat diamati, yang saya setujui adalah hal yang baik - jauh lebih sedikit kebisingan di sekitar berlangganan / berhenti berlangganan).

kurangnya langganan sinkron bisa menjadi masalah besar.

Saya pikir saya sudah mengerti bagaimana menyelesaikan "masalah keadaan" dan "Pemuatan Data Samping" (data turunan) secara konsisten. Itu membuatnya dalam "React-way" tanpa kewarganegaraan. Saya telah menemukan cara bagaimana mempertahankan konsistensi status kapan saja dan itu cocok dengan polanya UI = React(state) . Tidak mungkin saya kehabisan tangan untuk membuatnya benar-benar anti peluru, menambahkan lebih banyak contoh, dan membuat presentasi yang bagus. https://github.com/AlexeyFrolov/slt . Di sisi lain itu diuji dengan baik dan saya menggunakannya dalam proyek produksi saya secara berulang. Pikiran cerdas dipersilakan untuk berkontribusi.

Halo semua,

Lucu bahwa saya tidak tersandung di utas ini sebelumnya, karena di perusahaan kami, kami mengalami masalah yang sama yang ditangani oleh proposal ini setengah tahun yang lalu.
Kami mulai menggunakan React untuk proyek skala besar (pikirkan editor dengan kompleksitas Microsoft Visio, dengan data yang sangat siklus). Jumlah reaksi vanilla tidak sesuai dengan tuntutan kinerja kami,
dan fluks agak sulit bagi kami karena jumlah besar boilerplate dan rawan kesalahan dari semua langganan. Jadi kami menemukan bahwa kami juga membutuhkan struktur data yang dapat diamati.

Karena kami tidak dapat menemukan apa pun yang tersedia yang siap digunakan, kami membuat lib observables kami sendiri, berdasarkan prinsip-prinsip knockout observables (terutama: langganan otomatis).
Ini sebenarnya sangat cocok dengan siklus hidup komponen React saat ini, dan kami tidak memerlukan peretasan aneh atau bahkan panggilan balik rendering anak (PengamatMixin yang digunakan di bawah ini sekitar 10 loc).
Ini sangat meningkatkan DX kami dan bekerja sangat baik untuk tim kami, sehingga kami memutuskan untuk menerbitkannya sebagai open source . Sementara itu, ini cukup terbukti dalam pertempuran (misalnya, menyediakan polyfill array ES7 yang dapat diamati) dan sangat dioptimalkan.
Berikut adalah contoh penghitung waktu singkat, (juga tersedia sebagai JSFiddle ), IMHO DX tidak bisa jauh lebih baik... :lega:

var store = {};
// add observable properties to the store
mobservable.props(store, {
    timer: 0
});

// of course, this could be put flux-style in dispatchable actions, but this is just to demo Model -> View
function resetTimer() {
    store.timer = 0;
}

setInterval(function() {
    store.timer += 1;
}, 1000);

var TimerView = React.createClass({
    // This component is actually an observer of all store properties that are accessed during the last rendering
    // so there is no need to declare any data use, nor is there (seemingly) any state in this component
    // the combination of mobservable.props and ObserverMixin does all the magic for us.
    // UI updates are nowhere forced, but all views (un)subscribe to their data automatically
    mixins: [mobservable.ObserverMixin],

    render: function() {
        return (<span>Seconds passed: {this.props.store.timer}</span>);
    }
});

var TimerApp = React.createClass({
    render: function() {
        var now = new Date(); // just to demonstrate that TimerView updates independently of TimerApp
        return (<div>
            <div>Started rendering at: {now.toString()}</div>
            <TimerView {...this.props} />
            <br/><button onClick={resetTimer}>Reset timer</button>
        </div>);
    }
});

// pass in the store to the component tree (you could also access it directly through global vars, whatever suits your style)
React.render(<TimerApp store={store} />, document.body);

Untuk detail lebih lanjut tentang pendekatan ini, lihat blog ini . BTW, saya akan memastikan dekorator dan/atau wadah akan ditambahkan ke lib , bagi mereka yang menggunakan kelas ES6.

Sayangnya, saya tidak melihat utas ini sebelum react-europe, jika tidak, kami akan memiliki kesempatan bagus untuk membahas React & observables. Tapi terima kasih banyak untuk pembicaraan yang menginspirasi! :+1: Saya sangat menyukai abstraksi GraphQL dan pemikiran di balik Redux :)

@mweststrate Saya merasa komunitas akan berakhir dengan kebutuhan untuk memilih antara solusi "Dapat diamati" dan "Data yang tidak dapat diubah" pada akhirnya. Mungkin kita perlu mencampurkan beberapa cara untuk memiliki kekuatan dari kedua pendekatan dalam satu solusi (https://github.com/AlexeyFrolov/slt/issues/4). Dalam solusi saya, saya telah menerapkan pendekatan "Data yang tidak dapat diubah" dengan fokus pada konsistensi status kapan saja. Saya berencana untuk mendukung Observable dan Generator juga. Ini adalah contoh aturan yang digunakan untuk mengambil data "turunan" atau "tambahan" (seperti badan halaman, aset, rekomendasi, komentar, suka) dan menjaga konsistensi status aplikasi.

https://github.com/AlexeyFrolov/slt#rules -contoh

Ini adalah contoh kompleks dunia nyata (kode produksi saya) yang menunjukkan cara menangani pengalihan API dengan header Lokasi.

import r from "superagent-bluebird-promise";
import router from "./router";

export default {
    "request": function (req)  {
        let route = router.match(req.url);
        let session = req.session;
        route.url = req.url;
        return this
            .set("route", route)
            .set("session", req.session);
    },
    "route": {
        deps: ["request"],
        set: function (route, request) {
            let {name, params: { id }} = route;
            if (name === "login") {
                return this;
            }
            let url = router.url({name, params: {id}});
            let method = request.method ? request.method.toLowerCase() : "get";
            let req = r[method]("http://example.com/api/" + url);
            if (~["post", "put"].indexOf(method)) {
                req.send(request.body);
            }
            return req.then((resp) => {
                let ctx = this.ctx;
                let path = url.substr(1).replace("/", ".");
                if (!resp.body) {
                    let location = resp.headers.location;
                    if (location) {
                        ctx.set("request", {
                            method: "GET",
                            url: location.replace('/api', '')
                        });
                    }
                } else {
                    ctx.set(path, resp.body);
                }
                return ctx.commit();
            });
        }
    }
}

Selebihnya pendekatannya sama dengan milik Anda, kecuali tidak ada ikatan langsung ke React (tidak diperlukan dalam kasus saya). Saya percaya kita perlu menggabungkan kekuatan entah bagaimana untuk mengarsipkan tujuan bersama.

Saya pikir itu ide yang buruk karena akan ada banyak kasus tepi yang sulit untuk dipertimbangkan.

Dari komentar di atas saya dapat membuat daftar kemungkinan cakupan yang perlu dipikirkan oleh pengguna akhir setiap kali dia ingin membuat komponen "dataful":

  • Render sisi server
  • Inisialisasi .state dan .data
  • .context , .props , .state dan .observe keterjeratan
  • Render asinkron

Saya pikir proposal ini akan menyebabkan komponen rawan kesalahan, tidak stabil, dan sulit untuk di-debug.

Saya setuju dengan yang diusulkan oleh @glenjamin connect dan disconnect lifecycle hooks.

Maaf jika ini di luar topik (saya tidak tahu persis). Tapi inilah contoh mengapa saya pikir mengekspos API untuk berinteraksi dengan pohon komponen akan menjadi cara yang menarik untuk mendekati masalah ini: https://github.com/kevinrobinson/redux/blob/feature/loggit-todomvc/examples/loggit -todomvc/loggit/renderers/precompute_react_renderer.js#L72

Metode ini adalah peretasan saya untuk berjalan di atas pohon, jadi pertanyaan saya adalah bagaimana memiliki API publik untuk melakukan hal semacam ini memungkinkan inovasi di sini dalam "pemuatan data menyamping," yang menurut saya bermuara pada "perender yang tidak' bekerja dari atas ke bawah": https://github.com/kevinrobinson/redux/blob/feature/loggit-todomvc/examples/loggit-todomvc/loggit/react_interpreter.js#L8

Tapi inilah contoh mengapa saya pikir mengekspos API untuk berinteraksi dengan pohon komponen akan menjadi cara yang menarik untuk mendekati masalah ini.

Saya percaya @swannodette membicarakan hal ini di ReactConf. Saya ingin tahu apakah Om Next melakukannya?

Ya, dia menyarankan hal yang sama di ReactConf pertama. Saya belum pernah melihat Om.next terbaru atau mendengar pembicaraan EuroClojure, tetapi sebelumnya Om akan menggunakan strukturnya sendiri yang harus dibangun oleh pengguna untuk melakukan ini.

Metode ini adalah peretasan saya untuk berjalan di atas pohon, jadi pertanyaan saya adalah bagaimana memiliki API publik untuk melakukan hal semacam ini memungkinkan inovasi di sini dalam "pemuatan data menyamping," yang menurut saya bermuara pada "perender yang tidak' jangan bekerja dari atas ke bawah"

Saya pikir ini kira-kira setara dengan rendering dangkal yang ditemukan di utilitas pengujian - saya sebutkan menjadikan ini API kelas satu sebagai rekan DOM & String rendering ke @sebmarkbage di ReactEurope - perubahan 0,14 tampaknya membuka jalan dengan baik untuk ini.

Reaksi awal adalah bahwa ini mungkin level yang agak rendah - tetapi dengan cara yang mirip dengan hal-hal web yang dapat diperluas, saya pikir ini akan membuatnya lebih mudah untuk bereksperimen di ruang pengguna.

Saya setuju, jika kita memiliki cara untuk merender untuk mendapatkan pohonnya maka kita dapat berjalan dan menemukan semua kebutuhan data dan meneruskannya.

Mendapatkan akses ke seluruh pohon DOM virtual adalah fitur yang luar biasa kuat dan berguna yang ingin saya akses, bahkan jika itu diperlakukan sebagai masalah yang sama sekali terpisah.
Saya pikir itu masuk akal mengingat jalur yang diambil React dengan 0,14 dan seterusnya.

Mengingat kompleksitas yang dapat diamati, saya dengan rendah hati menyarankan tuan-tuan yang baik di utas ini untuk melihat implementasi data reaktif Meteor: https://github.com/meteor/meteor/wiki/Tracker-Manual. Rekonsiliasi dengan React membutuhkan 3-5 baris kode dalam metode componentWillMount , seperti:

  componentWillMount() {
    if (typeof this.getState === 'function') {
      Tracker.autorun(() => {
        // Assuming this.getState() calls some functions that return
        // reactive data sources
        this.setState(this.getState());
      });
    }
  }

Saya tidak tahu apakah Tracker (yang mudah diekstraksi sebagai perpustakaan terpisah) meniadakan kebutuhan akan dukungan yang dapat diamati di React, tetapi tampaknya seperti itu.

MOBservable mengikuti pola yang sangat mirip untuk menyegarkan komponen ke samping setelah beberapa nilai yang dapat diamati diubah, sehingga tampaknya metode siklus hidup + dekorator saat ini menawarkan fleksibilitas yang cukup bagi perpustakaan pihak ketiga untuk mengekspresikan pola semacam ini dan sebagai konsep sumber data ketiga hanya akan memperumit banyak hal.

    componentWillMount: function() {
        var baseRender = this.render;
        this.render = function() {
            if (this._watchDisposer)
                this._watchDisposer();
            var[rendering, disposer] = mobservableStatic.watch(() => baseRender.call(this), () => {
                    this.forceUpdate();
            });
            this._watchDisposer = disposer;
            return rendering;
        }
    },

@Mitranim Saya setuju, itu bacaan yang sangat bagus, terima kasih telah menemukannya! Ini secara efektif apa yang disarankan https://github.com/facebook/react/pull/3920 .

Kami belum memutuskan proposal mana yang lebih baik. Setelah bermain dengan keduanya, saya sebagian besar yakin bahwa pemrograman reaktif (seperti yang Anda tautkan; https://github.com/meteor/meteor/wiki/Tracker-Manual) adalah cara yang harus dilakukan, tetapi kami belum mencapai konsensus dan kami masih mencoba mencari tahu apa yang paling masuk akal, jadi kami menerima umpan balik.

@Mitranim @jimfb Saya telah menjadi penggemar berat Meteor selama beberapa tahun sekarang. Tracker sangat keren dan sangat mempesona. Saya membuat presentasi yang menunjukkan cara kerja versi pelacak yang sangat sederhana:

https://github.com/ccorcos/meteor-track/blob/master/client/main.js

Dan saya bahkan membuat perpustakaan aliran yang dapat diamati dengan Tracker:

https://github.com/ccorcos/meteor-tracker-streams

Tetapi karena saya telah sepenuhnya beralih menggunakan React alih-alih Blaze, saya menyadari bahwa Tracker terlalu rumit dan terkadang metode publish/subscribe/onChange yang sederhana hanya 100x lebih mudah.

Juga, untuk semua penggemar pemrograman fungsional, Tracker hadir dengan beberapa efek samping yang masuk akal 99% dari waktu, tetapi terkadang mereka membuat Anda mengerti. Begini tampilannya di komponen React

componentWillMount: ->
  <strong i="15">@c</strong> = Tracker.autorun =>
    @setState({loading: true})
    Meteor.subscribe 'users', => @setState({loading: false})
    Tracker.autorun =>
      @setState({users: Users.find({}, {sort:{name:-1}}).fetch()})
componentWillUnmount: ->
  @c.stop()

Maksud saya tentang efek samping adalah c.stop() akan menghentikan langganan dan juga autorun dalam.

@ccorcos Ya, di https://github.com/facebook/react/pull/3920 semua efek samping akan sepenuhnya internal untuk Bereaksi. Faktanya, inti React dapat mengimplementasikannya dengan cara yang sepenuhnya tidak dapat diubah/fungsional yang tidak akan melakukan mutasi apa pun. Tapi itu detail implementasi, karena efek sampingnya tidak akan terlihat secara eksternal.

Menarik... Jadi mengapa tidak menggunakan callback onChange saja? Saya telah menemukan mereka yang paling kompatibel. Baik Anda menggunakan Meteor (Pelacak), RxJS, Highland.js, dll., Anda selalu dapat berintegrasi dengan mereka secara sepele dengan panggilan balik acara. Dan sama dengan komponen orde tinggi React.

Apa yang saya suka tentang Redux adalah ia membuat logika ini keluar dari kerangka kerja dan menjaga komponen React sebagai fungsi murni.

@ccorcos Masalah utamanya adalah ketika instance komponen dihancurkan, semua "langganan" perlu dibersihkan. Penulis komponen sering melupakan pembersihan ini, sehingga membuat kebocoran memori. Kami menginginkannya otomatis, yang membuatnya lebih mudah untuk menulis (lebih sedikit boilerplate) dan lebih sedikit rawan kesalahan (pembersihan otomatis).

@jimfb Tepat. Berhenti berlangganan otomatis adalah salah satu hal yang sangat menarik tentang pendekatan Tracker. Setiap panggilan ke sumber data reaktif membangun kembali langganan, dan begitu komponen berhenti memanggil data, tidak ada yang perlu dibersihkan!

Ya, tapi itu tidak benar-benar "secara otomatis" berhenti berlangganan. Anda harus memanggil c.stop() di komponen yang akan di-unmount. Anda dapat mengabstraksikannya dengan mixin.

componentWillMount: ->
  <strong i="7">@autorun</strong> =>
    @setState({loading: true})
    Meteor.subscribe 'users', => @setState({loading: false})
    Tracker.autorun =>
      @setState({users: Users.find({}, {sort:{name:-1}}).fetch()})

Tapi ini benar-benar tidak berbeda dari api lainnya. Anda dapat membuat metode built-in yang secara otomatis berhenti berlangganan untuk Anda saat unmount, dll. Dengar, saya penggemar berat Meteor. Jadi saya tidak ingin berbicara Anda keluar dari ini. Hanya saja terkadang, saya merasa sangat sulit untuk menjelaskan hal ini kepada seseorang yang tidak terbiasa dengannya. Sementara itu, menggunakan pendengar/pemancar peristiwa sederhana jauh lebih sederhana untuk dipahami dan diterapkan, dan mereka cenderung sangat kompatibel dengan sistem reaktivitas apa pun yang ingin Anda gunakan...

@ccorcos Kita mungkin berbicara tentang mekanisme yang berbeda. Terakhir saya periksa, Tracker tidak memiliki langganan permanen; setiap fungsi yang membuat ketergantungan pada sumber data reaktif (dengan mengaksesnya) dijalankan kembali _sekali_ ketika berubah, dan itulah akhir dari langganan. Jika mengakses sumber data lagi, ini menetapkan kembali "langganan" untuk satu perubahan lagi. Dan seterusnya.

@Mitranim benar, semantik #3920 lebih "otomatis" daripada Meteor (berhenti berlangganan benar-benar otomatis), dan jauh lebih sederhana karena secara harfiah tidak ada area permukaan API dalam kasus penggunaan umum.

@ccorcos @Mitranim Untuk perpustakaan terinspirasi Tracker / Vue.js yang siap digunakan, Anda dapat mencoba Mobservable , ia mengamati semua data yang diakses selama _render_, dan membuang semua langganan saat dilepas (sampai saat itu langganan tetap hidup). Kami berhasil menerapkannya pada proyek yang cukup besar sejauh ini di Mendix. Ini cukup tidak mengganggu data Anda serta menghiasi objek yang ada alih-alih menyediakan objek modelnya sendiri.

Terakhir saya periksa, Tracker tidak memiliki langganan permanen

Langganan @Mitranim bisa permanen. Lihat ini.

sub = Meteor.subscribe('chatrooms')
# this subscription lasts until...
sub.stop()

Sekarang ada beberapa hal menarik dengan Tracker. Lihat ini.

comp = Tracker.autorun ->
  Meteor.subscribe('chatrooms')
# this subscription lasts until...
comp.stop()

Contoh terakhir tidak terlalu berguna. Sampai kita melakukan hal seperti ini.

roomId = new ReactiveVar(1)
comp = Tracker.autorun ->
  Meteor.subscribe('messages', roomId.get())
# when I change the roomId, the autorun will re-run
roomId.set(2)
# the subscription to room 1 was stopped and now the subscription to room 2 has started
# the subscription is stopped when I call stop...
comp.stop()

setiap fungsi yang membuat ketergantungan pada sumber data reaktif (dengan mengaksesnya) dijalankan kembali sekali ketika berubah, dan itulah akhir dari langganan.

Langganan berlangsung hingga autorun dijalankan kembali (ketergantungan reaktif berubah) atau komputasi yang dijalankannya berhenti. Keduanya memanggil hook komputasi.onInvalidate.

Inilah versi super yang dibuat-buat yang mencapai hal yang sama persis. Semoga ini akan membantu Anda memahami cara kerja Tracker. Dan mungkin Anda juga akan melihat betapa berantakannya hal itu.

comp = Tracker.autorun ->
  Meteor.subscribe('chatrooms')

# is the same as

comp = Tracker.autorun (c) ->
  sub = null
  Tracker.nonreactive ->
    # dont let comp.stop() stop the subscription using Tracker.nonreactive
    sub = Meteor.subscribe('chatrooms')
  c.onInvalidate ->
    # stop the subscription when the computation is invalidated (re-run)
    sub.stop()
# invalidate and stop the computation
comp.stop()

@ccorcos Saya melihat sumber kebingungan sekarang; itu adalah kosakata saya. Saat berbicara tentang langganan, maksud saya bukan _Meteor subscriptions_. Mereka menentukan data apa yang didorong dari server ke klien, tetapi tidak relevan dengan pembaruan lapisan tampilan, yang merupakan subjek diskusi ini. Saat mengatakan _subscription_, saya menggambar paralel antara pendengar acara tradisional dan kemampuan Tracker untuk menjalankan kembali fungsi yang memiliki ketergantungan pada sumber data reaktif. Dalam kasus komponen React, itu akan menjadi metode komponen yang mengambil data dan kemudian memanggil setState atau forceUpdate .

@mweststrate Terima kasih untuk contohnya, kelihatannya menarik.

Ah iya. Jadi mereka punya cara cerdas untuk melakukannya.

componentWillMount: function() {
  this.comp = Tracker.autorun(() => {
    let sub = Meteor.subscribe('messages')
    return {
      loading: !sub.ready(),
      messages: Messages.find().fetch()
    }
  })
componentWillUnmount: function() {
  this.comp.stop()
}

Langganan hanya akan berlangganan kembali setiap kali tanpa masalah. sub.ready dan Messages.find.fetch keduanya "reaktif" dan akan memicu autorun untuk dijalankan kembali setiap kali mereka berubah. Apa yang keren tentang Tracker adalah ketika Anda mulai menyembunyikan autoruns dan hanya memiliki dokumentasi bahwa fungsi tertentu berada dalam "konteks reaktif"

Anda bisa memasukkan ini ke dalam mixin

componentWillMount: function() {
  this.comp = Tracker.autorun(() => {
    return this.getReactiveData()
  })
componentWillUnmount: function() {
  this.comp.stop()
}

Dan kemudian Anda memiliki fungsi reaktif ajaib yang berfungsi!

getReactiveData: function() {
  let sub = Meteor.subscribe('messages')
  return {
    loading: !sub.ready(),
    messages: Messages.find().fetch()
  }
}

Trackernya keren banget kaya gini...

@ccorcos Ternyata saya agak salah tentang unsubs otomatis saat menggunakan eventing gaya Tracker. Anda masih perlu menghentikan pendengar di componentWillUnmount , jika tidak, pendengar akan terus menjangkau sumber data dan memanggil setState (kecuali jika Anda memeriksa dengan isMounted() yang sekarang tidak digunakan lagi).

Sebagai tambahan, Anda dapat membuat dekorator elegan untuk membuat metode komponen reaktif. Berikut beberapa contohnya: [1] , [2] . Terlihat seperti ini:

export class Chat extends React.Component {
  <strong i="13">@reactive</strong>
  updateState () {
    this.setState({
      auth: auth.read(),
      messages: messages.read()
    })
  }

  /* ... */
}

@Mitranim cukup rapi - Saya lebih suka fungsi tingkat tinggi. ;)

@sebmarkbage @jimfb Saya telah mengikuti utas ini dan utas alt (#3858) selama beberapa bulan sekarang, dan saya ingin tahu apakah tim inti telah mencapai konsensus tentang masalah ini, atau setidaknya arahan umum.

@oztune Tidak ada pembaruan; kami telah fokus pada prioritas lain. Kami akan memposting ke salah satu utas ketika ada pembaruan tentang topik ini.

Kawan, saya telah membuat API universal untuk membuat wadah. Periksa react-komposer . Yang itu, kita bisa membuat wadah dengan fungsi urutan yang lebih tinggi.

import { compose } from `react-komposer`;

// Create a component to display Time
const Time = ({time}) => (<div>{time}</div>);

// Create the composer function and tell how to fetch data
const composerFunction = (props, onData) => {
    const handler = setInterval(() => {
    const time = new Date().toString();
    onData(null, {time});
  }, 1000);

  const cleanup = () => clearInterval(handler);
  return cleanup;
};

// Compose the container
const Clock = compose(composerFunction)(Time);

// Render the container
ReactDOM.render(<Clock />, document.getElementById('react-root'));

Inilah versi langsungnya: https://jsfiddle.net/arunoda/jxse2yw8/

Kami juga memiliki beberapa cara mudah untuk membuat container dengan Promises, Rx.js Observables, dan With Meteor's Tracker .

Lihat juga artikel saya tentang ini: Mari Membuat Beberapa Wadah Bereaksi

@arunoda Kami akhirnya melakukan sesuatu yang sangat mirip. Satu hal yang saya ingin tahu adalah bagaimana Anda mencegah composerFunction dipanggil pada setiap perubahan prop?

@oztune Sebenarnya sekarang akan berjalan lagi. Kami menggunakan Lokka dan Meteor ini. Keduanya memiliki cache lokal dan tidak mengenai server bahkan ketika kami memanggil composerFunction beberapa kali.

Tapi saya pikir kita bisa melakukan sesuatu seperti ini:

const options =  {propsToWatch: ["postId"]};
const Clock = compose(composerFunction, options)(Time);

Ada ide?

@arunoda Itulah yang kami coba juga, tetapi ini menciptakan sedikit keterputusan antara tindakan dan dependensinya. Kami sekarang melakukan sesuatu yang mirip dengan react-async, di mana, alih-alih segera melakukan tindakan yang diberikan, composerFunction akan mengembalikan fungsi dan kunci. Jika kunci berbeda dari kunci sebelumnya yang dikembalikan oleh composerFunction, fungsi baru akan dijalankan. Saya tidak yakin apakah ini merupakan garis singgung dari utas github ini, jadi saya akan senang untuk melanjutkan di Twitter (nama pengguna yang sama).

@oztune Saya membuat masalah GH baru dan mari kita lanjutkan obrolan kita di sana. Jauh lebih baik daripada twitter saya kira.

Mengapa tidak membiarkan JSX memahami dan merender Observables secara langsung sehingga saya dapat melewatkan Observable dalam props, misalnya this.props.todo$ dan menyematkannya di JSX. Maka saya tidak memerlukan API apa pun, sisanya dikelola di luar React dan HoC digunakan untuk mengisi yang dapat diamati. Seharusnya tidak masalah jika alat peraga berisi data biasa atau yang dapat diamati maka tidak diperlukan this.data khusus.

{this.props.todo$

Selain itu React render dapat merender Oservable[JSX] yang memungkinkan desain dijelaskan dalam tautan tanpa pustaka tambahan.

https://medium.com/@milankinen/containers -are-dead-long-live-observable-combinators-2cb0c1f06c96#.yxns1dqin

https://github.com/milankinen/react-combinators

Halo,
Untuk saat ini kita dapat menggunakan komponen stateless dengan aliran rxjs dengan mudah.
Saya tidak mengerti perlunya API lain.
Saya menulis sebuah contoh - papan yang dapat Anda gerakkan mouse di atasnya dan ketika mencapai 26 itu berubah untuk memulai kembali.
Saya akan senang mendengar apa yang Anda pikirkan tentang cara ini.
Berikut kodenya:
https://jsfiddle.net/a6ehwonv/74/

@giltig : Saya belajar cara ini akhir-akhir ini juga dan menyukainya. Bersama dengan Cycle.js.

Saya akan sangat menghargai untuk dapat mendengarkan event handler yang didefinisikan pada komponen dengan mudah tanpa harus melewati Subjek untuk menjembatani. Jika saya mengerti dengan benar, itu adalah kebalikan dari apa yang disarankan di sini. Atau, jika kita dapat mengamati React vdom untuk kejadian sintetiknya, mungkin "ref" dapat digunakan untuk mengamati untuk menghindari tag CSS hanya untuk mengamati.

RxJ lebih lanjut dapat mendukung objek di combineLatest sehingga kita dapat menggunakan komponen fungsional React secara langsung untuk membuat komponen yang lebih besar.

const myFancyReactComponent = ({surface, number, gameover}) => (
        <div> 
          {gameover ? gameover : surface}
          {number}
        </div>
)

const LiveApp = Rx.Observable.combineLatest(
    LiveSurface, DynamicNumberView, DynamicGameOver,
    myFancyReactComponent
)

Hai,
Alasan kami tidak menggunakan Cycle atau kerangka kerja lainnya adalah karena saya lebih suka pustaka daripada kerangka kerja (lebih banyak kontrol untuk pengembang) dan saya juga ingin menikmati kekuatan komunitas yang dimiliki React.
Begitu banyak mesin render yang sekarang dikembangkan untuk React dan sangat disayangkan untuk tidak menggunakannya (ReactNative, ReactDom, ReactThree dll.). Cukup mudah menggunakan Rxjs dan bereaksi seperti contoh yang saya tunjukkan di atas.

Itu akan membuat berpikir lebih mudah jika komponen reaksi dapat menerima pojo selama dapat diamati sebagai alat peraga. Adapun untuk saat ini tidak mungkin jadi apa yang saya tunjukkan di atas adalah cara yang kami pilih.

BTW apa yang Anda lakukan dengan MyFancyReactComponent adalah mungkin dan kami benar-benar melakukannya juga dalam beberapa kasus meskipun Anda juga dapat menulis jsx secara langsung.

Mengenai mata pelajaran - saya pikir ini cara yang valid karena pada akhirnya di komponen Bereaksi saya hanya menggunakan fungsi handler yang bisa berupa apa saja. Saya memilih untuk mengimplementasikannya dengan subjek di dalam yang menerima acara tetapi orang lain dapat memilih sebaliknya - itu fleksibel.

Itu akan membuat berpikir lebih mudah jika komponen reaksi dapat menerima pojo selama dapat diamati sebagai alat peraga. Adapun untuk saat ini tidak mungkin jadi apa yang saya tunjukkan di atas adalah cara yang kami pilih.

alat peraga yang dapat diamati tidak masuk akal dalam jangka panjang. Tidak ada artinya sama sekali dalam konteks itu, sebenarnya..

Kedengarannya bagi saya seperti kami berakhir dengan model yang berbeda dengan Ketegangan (cache + konteks). Cache itu sendiri mungkin mendapatkan dukungan untuk langganan. Anda dapat melacak pekerjaan yang tersisa untuk Suspense di https://github.com/facebook/react/issues/13206.

Kami juga menyediakan paket berlangganan untuk kasus yang lebih terisolasi.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat