React: Mendukung rendering server asinkron (menunggu data sebelum rendering)

Dibuat pada 24 Jun 2014  ·  139Komentar  ·  Sumber: facebook/react

Ini akan sangat memudahkan proses membangun sesuatu yang isomorfik jika componentWillMount dapat mengembalikan janji dan reaksi itu akan menunda rendering hingga janji itu diselesaikan. Saya telah melihat upaya melakukan sesuatu seperti itu di react-router dan rrouter, namun memberikan tanggung jawab ini kepada setiap komponen alih-alih modul router akan lebih masuk akal bagi saya.

Component API Server Rendering Backlog Feature Request

Komentar yang paling membantu

Beberapa bulan yang lalu saya memberikan ceramah di JSConf Islandia yang menjelaskan fitur rendering async yang akan datang di React (lihat bagian kedua): https://reactjs.org/blog/2018/03/01/sneak-peek-beyond-react -16.html. Ini tentang pengambilan data sisi klien.

Sekarang @acdlite memberikan ceramah tentang bagaimana konsep yang sama dapat diterapkan untuk mengaktifkan menunggu data secara asinkron di server dalam komponen React, dan secara bertahap menghapus markup saat sudah siap: https://www.youtube.com/watch?v= z-6JC0_cons

Harap Anda akan menikmati menonton pembicaraan ini! Saya pikir dalam satu tahun atau lebih kami mungkin dapat menyelesaikan masalah ini dan memiliki strategi resmi untuk ini.

Semua 139 komentar

Alasan utama (saya percaya) bahwa ini belum ada adalah bahwa di sisi klien, pada dasarnya Anda selalu ingin menunjukkan semacam indikator pemuatan alih-alih menunda rendering. (Itu juga akan membuat kode menjadi lebih kompleks secara signifikan, tetapi kita mungkin bisa mengatasinya.)

Ada 2 kasus yang sulit saya selesaikan tanpa itu:

  • Di sisi server, jika Anda ingin mengambil data sebelum rendering, Anda tidak dapat mendelegasikan pengambilan data ke komponen karena Anda tidak memiliki informasi tentang komponen mana yang akan dirender
  • Di sisi klien saat pertama kali Anda memasang aplikasi setelah menerima html yang telah dirender sebelumnya, bahkan jika Anda memiliki semacam cache dari data yang diambil di server, Anda mungkin ingin menggunakan metode async untuk mengambil data tersebut, dan itu akan mencegah reaksi dari menggunakan kembali html.

react-async menyelesaikan masalah tersebut dengan serat, dan cache. Itu berhasil tetapi menurut saya itu hanya solusi _hackish_ untuk memecahkan masalah yang hanya bisa diselesaikan di inti.

Warnai saya tanpa informasi tentang hal ini, @fdecampredon mengatakan bahwa componentWillMount adalah async dan Anda tidak segera mengembalikan apa pun, apa yang harus dirender oleh React sampai tidak ada, tidak ada? Jika demikian, mengapa tidak mengembalikan apa pun di render jika belum ada data? (Ya, saya mendapatkan sisi server) Juga, apa yang harus terjadi jika properti berubah sebelum componentWillMount diaktifkan?

Secara pribadi, sepertinya salah mengirimkan permintaan asinkron selama componentWillMount kecuali komponen tersebut benar-benar merupakan kotak hitam yang terisolasi dan juga mengimplementasikan indikator pemuatan. Sejauh yang saya pahami, komponen React tidak boleh disalahartikan sebagai instance OOP yang lebih konvensional. Dalam kasus terbaik, komponen React adalah alat untuk memvisualisasikan data dalam props, jika interaktif maka mungkin juga menyatakan. Ini adalah tampilan, bukan tampilan dan model.

Di telinga saya, itu terdengar seperti masalahnya, komponen React seharusnya bukan yang mengirimkan permintaan async, Anda mengambil semua data dan ketika data itu siap, baru kemudian Anda memanggil React.renderComponent . Solusi sisi klien dan sisi server yang sama. Anda juga mendapatkan kemampuan untuk membatalkan dengan hasil pilihan Anda jika ada permintaan async yang gagal.

Jangan ragu untuk mengabaikan saya jika saya salah memahami sesuatu, tetapi tampaknya Anda memperlakukan komponen Bereaksi sebagai tampilan dan model, ketika (tampaknya) itu dimaksudkan hanya sebagai tampilan.

Warnai saya tanpa informasi tentang hal ini, @fdecampredon mengatakan bahwa componentWillMount adalah async dan Anda tidak segera mengembalikan apa pun, apa yang harus dirender oleh React sampai ada, tidak ada apa-apa? Jika demikian, mengapa tidak mengembalikan apa pun di render jika belum ada data? (Ya, saya mendapatkan sisi server) Juga, apa yang harus terjadi jika props berubah sebelum componentWillMount menyala?

Saya harus mengakui bahwa saya tidak memikirkan semua kasus ^^.
Fitur ini hanya akan berguna saat pertama kali kita memasang komponen tingkat atas, dan di server, memang benar bahwa jika tidak, di sebagian besar pemeran, Anda ingin menampilkan indikator pemuat.

Secara pribadi, sepertinya salah mengirimkan permintaan asinkron selama componentWillMount kecuali komponen tersebut benar-benar kotak hitam yang terisolasi dan juga mengimplementasikan indikator pemuatan. Sejauh yang saya pahami, komponen React tidak boleh disalahartikan sebagai instance OOP yang lebih konvensional. Dalam kasus terbaik, komponen React adalah alat untuk memvisualisasikan data dalam props, jika interaktif maka mungkin juga menyatakan. Ini adalah tampilan, bukan tampilan dan model.

Dengan satu atau lain cara, Anda ingin komponen 'tingkat atas' dapat mengambil data, seperti yang dilakukan pada sampel Flux .
Dalam contoh ini hal-hal cukup sederhana karena mengambil daftar todo adalah operasi sinkron, jika tidak, dan dalam kasus pra-rendering di server, kami akan membuat pertama kali tanpa data (dan kehilangan pra-render markup dari server).

Dalam kasus aplikasi sederhana dengan satu set data yang ditampilkan oleh satu hierarki tampilan, masih tidak ada banyak masalah, Anda dapat memuat data terlebih dahulu dan tetap menyimpan properti sinkron dari toko Anda.
Sekarang dalam kasus aplikasi yang terdiri dari beberapa modul yang Anda gunakan kembali di seluruh aplikasi Anda, saya ingin dapat memperlakukan modul-modul itu sebagai aplikasi terpisah yang dapat 'berlangganan' di toko yang berbeda (yang akan bertanggung jawab untuk mengambil data).

Mungkin saya mendapatkan hal-hal dengan cara yang salah tetapi beberapa diskusi/contoh di web membuat saya berpikir ada sesuatu yang hilang di suatu tempat:

  • Dalam beberapa sampel , menurut saya @petehunt mencoba mencapai sesuatu yang serupa.
  • react-nested-router mempromosikan beberapa mekanisme serupa di willTransitionTo , dan beberapa diskusi membuat saya merasa bahwa tidak ada yang datang dengan solusi yang tepat.
  • RRouter juga menyediakan semacam mekanisme untuk mengambil data terlebih dahulu saat komponen sedang dirender/dipasang.
  • dan akhirnya react-async seperti yang saya katakan sebelumnya.

@fdecampredon Untuk lebih jelasnya, tujuan willTransitionTo di react-nested-router adalah _not_ untuk memuat data, khususnya karena mengembalikan janji dari metode itu memang akan memblokir rendering UI baru, yang tidak ingin Anda lakukan kecuali Anda benar-benar harus.

@fdecampredon Semua orang masih mencoba mencari tahu, jadi tidak akan mengejutkan saya jika tidak ada yang memiliki jawaban pasti. Tapi saya kira para pengembang di Facebook pasti pernah mengalami ini sendiri beberapa kali.

Ada pembaruan tentang ini? Saya baru saja mulai menjelajahi React dan saya langsung mengalami ini. Banyak orang merekomendasikan React sebagai solusi untuk membangun aplikasi isomorfik, tetapi selama ini tidak diselesaikan, saya pikir itu tidak dapat menyelesaikan pekerjaan.

Di telinga saya, kedengarannya seperti masalah, komponen React seharusnya bukan yang mengirimkan permintaan async, Anda mengambil semua data dan ketika data itu siap, baru kemudian Anda memanggil React.renderComponent. Solusi sisi klien dan sisi server yang sama. Anda juga mendapatkan kemampuan untuk membatalkan dengan hasil pilihan Anda jika ada permintaan async yang gagal.

Jangan ragu untuk mengabaikan saya jika saya salah memahami sesuatu, tetapi tampaknya Anda memperlakukan komponen Bereaksi sebagai tampilan dan model, ketika (tampaknya) itu dimaksudkan hanya sebagai tampilan.

Jika ini benar, maka React tidak lebih dari solusi templating/lapisan tampilan yang sedikit berbeda. Dan itu akan memalukan karena ada potensi seperti itu. Saya sangat mengerti @fdecampredon ketika dia menyebutkan aplikasi kompleks yang terdiri dari banyak modul. React akan sempurna untuk ini.

Saya tidak berpikir bahwa pendekatan ini berarti memperlakukan komponen sebagai tampilan dan model. Jika Anda melihat arsitektur Flux, mereka menganggap komponen React sebagai _controller-views_ yang tidak hanya menampilkan data (dari toko) tetapi juga memanggil tindakan berdasarkan interaksi pengguna. Dan tindakan kemudian perbarui toko (= model). Bagi saya itu terdengar seperti arsitektur MVC yang jelas.

Masalahnya adalah dengan tindakan awal yang mengisi toko. Ini cukup sederhana di sisi klien di mana tindakan dapat dipanggil dari metode componentDidMount() seperti yang direkomendasikan. Di sisi server di sisi lain, kami benar-benar membutuhkan beberapa tempat khusus dan semacam mekanisme yang akan menunda rendering hingga tindakan selesai dan toko terisi.

Saat ini, menurut saya satu-satunya cara adalah React-async/Fibers + Flux. Hal yang menyenangkan tentang Flux adalah kita tidak memerlukan cache buatan untuk memindahkan status komponen dari server ke klien (seperti yang dilakukan pada contoh react-async asli), kita cukup menginisialisasi penyimpanan dan kemudian mengirimkannya ke klien dengan markup html (lihat contoh ini ).

Tapi solusi ini memang _hackish_.

Saya tidak berpikir itu perlu componentWillMount yang async; Saya bahkan tidak yakin itu perlu menjadi acara siklus hidup. Masalah sebenarnya adalah bahwa di sisi server tidak ada cara untuk menganalisis pohon komponen sampai setelah semuanya dirender ke string.

Solusi ideal saya untuk menyelesaikan ini adalah dengan mengizinkan "rendering" yang hanya akan membangun pohon komponen, kemudian kita akan dapat melintasi pohon untuk menemukan komponen yang memerlukan data tambahan, memungkinkan kita memuat lebih banyak data secara asinkron dan "re-render " subpohon komponen itu, dan kemudian setelah kita siap untuk menghapus markup, izinkan kita mengonversi pohon itu menjadi string.

Ini mereplikasi apa yang dapat kami lakukan di browser: memiliki DOM virtual yang dapat kami render ulang sesuai keinginan. Perbedaannya adalah bahwa di browser pembaruan DOM bisa implisit. Di server, kita harus eksplisit tentang kapan kita merender ke string sehingga kita bisa melakukan pembaruan ke DOM virtual berdasarkan data async.

Solusi ideal saya untuk menyelesaikan ini adalah dengan mengizinkan "rendering" yang hanya akan membangun pohon komponen, kemudian kita akan dapat melintasi pohon untuk menemukan komponen yang memerlukan data tambahan, memungkinkan kita memuat lebih banyak data secara asinkron dan "re-render " subpohon komponen itu, dan kemudian setelah kita siap untuk menghapus markup, izinkan kita mengonversi pohon itu menjadi string. — @mridgway

Ya, itu bagus. Saat ini, merender komponen dua kali di sisi server dapat digunakan sebagai solusi.

Saya ingin merujuk react-nexus sebagai contoh dari apa yang saya ingin dukung dalam React. Ini pada dasarnya adalah penulisan ulang tentang cara kerja mountComponent kecuali bahwa ia membangun pohon komponen tanpa benar-benar memasangnya ke DOM atau menulis string. Ini memungkinkan Anda untuk melintasi pohon komponen dan menjalankan metode asinkron saat pohon sedang dibangun. Masalah dengan implementasi ini adalah bahwa ia membuang pohon pertama itu dan kemudian memanggil React.renderToString , sedangkan akan lebih baik untuk mengambil pohon pra-render itu dan merender/memasangnya.

Saya bersedia mengerjakan ini di dalam inti, tetapi akan membutuhkan beberapa petunjuk. Saya pikir salah satu persyaratannya adalah membuat ReactContext tidak direferensikan secara global sehingga async tidak menyebabkan masalah. Apakah ada global lain yang mungkin mengalami masalah dengan ini juga?

@mridgway Jika saya tidak salah global ReactContext mungkin merupakan hal yang kompatibel dan akan hilang dalam 0,14. Tapi saya _mungkin_ salah.

@gaearon Ya, itulah pengertian yang saya dapatkan dari penghentian withContext.

:+1: Menggunakan react-router untuk ATM ini. Pendekatan @mridgway terdengar sangat masuk akal.

Pandangan yang sedikit berbeda tentang masalah ini adalah bagaimana menangani pemuatan asinkron dari potongan kode yang dihasilkan oleh bundler (seperti webpack).

Seperti yang dibahas dalam tiket ini - https://github.com/rackt/react-router/issues/1402 - masalah dengan dukungan rendering async adalah render awal tampaknya nol karena potongan yang relevan belum dimuat di awal render pass, menghasilkan <noscript> di root DOM dan checksum gagal. Semuanya jatuh ke tempatnya dengan cepat setelah itu, tetapi itu menghasilkan kedipan di UI saat bekerja secara lokal dan lebih buruk di lapangan, terutama jika potongan yang akan diunduh berukuran wajar (katakanlah> 30KB)

Kemampuan untuk membagi aplikasi besar menjadi beberapa bagian penting bagi kami dan setelah memecahkan masalah pengambilan data (kami menggunakan router reaksi dan rute bersarang yang dapat dilalui sebelum rendering di server untuk mengambil dependensi data) ini adalah bagian terakhir teka-teki yang menghalangi kami untuk bergerak sepenuhnya ke solusi React untuk front-end kami.

@anatomic Itu bukan tanggung jawab React, adalah tugas Anda untuk memotong dengan tepat dan menunda rendering sampai semua potongan yang diperlukan telah dimuat. Dengan kata lain, jika salah satu komponen Anda memiliki ketergantungan pada beberapa perpustakaan eksternal, jelas merupakan masalah Anda untuk memuaskan sebelum mencoba menggunakannya, React tidak dapat melakukannya meskipun telah dicoba, jadi hal yang sama berlaku di seluruh papan.

Jangan ragu untuk menerapkan strategi alternatif yang mungkin lebih cocok untuk Anda, misalnya <WaitFor for={MyAsyncLoadedCompSignal} until={...} then={() => <MyAsyncLoadedComp ... />} /> . Tetapi ini secara inheren berpendirian dan bukan sesuatu yang harus atau bahkan perlu ditawarkan oleh React, jadi sebaiknya diserahkan kepada komunitas.

Lebih baik menyimpan hal-hal asinkron di luar cakupan komponen Bereaksi, berikut adalah contohnya:

import React from 'react';
import Layout from './components/Layout';
import NotFoundPage from './components/NotFoundPage';
import ErrorPage from './components/ErrorPage';

const routes = {

  '/': () => new Promise(resolve => {
    require(['./components/HomePage'], HomePage => { // Webpack's script loader
      resolve(<Layout><HomePage /></Layout>);
    });
  }),

  '/about': () => new Promise(resolve => {
    require(['./components/AboutPage'], AboutPage => { // Webpack's script loader
      resolve(<Layout><AboutPage /></Layout>);
    });
  })

};

const container = document.getElementById('app');

async function render() {
  try {
    const path = window.location.hash.substr(1) || '/';
    const route = routes[path];
    const component = route ? await route() : <NotFoundPage />;
    React.render(component, container);
  } catch (err) {
    React.render(<ErrorPage error={err} />, container);
  }
}

window.addEventListener('hashchange', () => render());
render();

Lihat Pemisahan Kode Webpack , Perutean React.js dari Awal dan perutean reaksi (segera hadir)

@syranide Saya akan terus mengerjakannya, tetapi saya tidak berpikir itu biner seperti yang Anda katakan di atas. Kami menggunakan react-router sehingga dapat menimbulkan beberapa masalah pada campuran karena router adalah komponen daripada duduk di luar lingkungan React.

Jika kita melakukan pendekatan <WaitFor ... /> , pasti kita masih akan mendapatkan DOM yang berbeda pada render pertama yang masih akan menyebabkan konten berkedip/hilang?

Jika kita melakukan pendekatan, pasti kita masih akan mendapatkan DOM yang berbeda pada render pertama yang masih akan menyebabkan kedipan/hilangnya konten?

Jika Anda tidak ingin berkedip (beberapa melakukannya) itu hanya masalah menunggu semua potongan yang Anda andalkan telah dimuat sebelum rendering, webpack menyediakan ini dengan require.resolve .

PS. Ya, react-router dan apa pun yang Anda gunakan pasti memperumit solusi, tetapi itu masih bukan masalah React untuk dipecahkan.

Jika Anda tidak ingin berkedip (beberapa melakukannya) itu hanya masalah menunggu semua potongan yang Anda andalkan telah dimuat sebelum rendering, webpack menyediakan ini di luar kotak dengan require.resolve.

Saya akan memeriksanya, pemahaman saya tentang require.resolve adalah bahwa itu adalah pencarian sinkron dari id modul dan tidak melibatkan perjalanan ke server? Kami menggunakan require.ensure untuk mengelola pemuatan chunk kami.

Melihat kode kami lagi, saya pikir kami dapat mengatasi masalah dengan membuat router reaksi berpikir itu dirender di server tetapi kemudian mengikuti pendekatan sisi klien standar:

const history = new BrowserHistory();

if (typeof history.setup === "function") {
    history.setup();
}

Router.run(routes, history.location, (err, initialState, transition) => {
    React.render(<Provider redux={redux}>{() => <Router key="ta-app" history={history} children={routes} />}</Provider>, document.getElementById('ta-app'));
});

Saya akan memeriksanya, pemahaman saya tentang require.resolve adalah bahwa itu adalah pencarian sinkron dari id modul dan tidak melibatkan perjalanan ke server? Kami menggunakan require.ensure untuk mengelola pemuatan chunk kami.

Maaf, ya maksud saya require.ensure . Callback dijalankan hanya ketika semua dependensi terpenuhi, jadi ini hanya masalah menempatkan render/setState di dalamnya.

Ok, begitulah cara kami melakukannya, terima kasih atas balasan Anda. Ini terasa seperti sesuatu yang perlu ditangani di react-router jadi saya akan melanjutkan diskusi di sana - maaf jika ini adalah tempat yang salah untuk melakukan percakapan ini!

Anda benar bahwa require.ensure adalah cara untuk pergi, saya kira masalah utama kami adalah bahwa itu harus ditautkan ke rute yang sedang dikunjungi sehingga langsung terikat ke router. Dengan react-router berbasis komponen yang mengikatnya ke pohon render. Tanpa retasan saya di atas, kita dibiarkan berjuang untuk melihat pohon sebelum semuanya dimuat async (atau menduplikasi logika perutean untuk memungkinkan potongan yang relevan dimuat di tingkat atas).

Anda benar bahwa require.ensure adalah cara yang harus ditempuh, saya kira masalah utama kami adalah bahwa itu harus ditautkan ke rute yang sedang dikunjungi sehingga langsung terikat ke router. Dengan react-router berbasis komponen yang mengikatnya ke pohon render. Tanpa retasan saya di atas, kita dibiarkan berjuang untuk melihat pohon sebelum semuanya dimuat async (atau menduplikasi logika perutean untuk memungkinkan potongan yang relevan dimuat di tingkat atas).

Saya tidak terlalu akrab dengan react-router, tetapi saya masih membayangkannya sebagai kasus setRoute(...) => require.ensure([], function() { require(...); setRoute(...); }) yang sebenarnya tidak praktis sehingga Anda memperkenalkan tingkat tipuan lain. Sebuah peta rute dan async require.ensure loader untuk masing-masing. Tulis helper function asyncSetRoute(...) { loadRoute(route, function() { setRoute(...); }} , sekarang Anda memanggil asyncSetRoute sebagai gantinya dan itu akan menunda pembaruan router sampai semuanya siap.

Kode semu dan jenis generik, tapi itu sepertinya pendekatan keseluruhan bagi saya. Mungkin react-router harus menyediakan ini, mungkin tidak... mungkin idealnya disediakan sebagai pembantu eksternal, saya tidak yakin.

Jadi setelah berjam-jam meneliti, saya baru saja mengonfirmasi bahwa rendering sisi server adalah _impossible _ kecuali Anda memberi makan semuanya dari atas ke bawah (?).

Kemungkinan solusi jangka pendek:
A. Pra-isi toko dan buat pemuatan sisi server sinkron

B. Umpankan semuanya dari atas sebagai satu input data setelah asinkron mengambil satu objek data Anda.

kembali: 'A'. Ini tidak akan berfungsi kecuali Anda sudah tahu seperti apa struktur render Anda nantinya. Saya perlu merendernya untuk mengetahui berbagai dependensi koleksi/model/api saya. Juga, bagaimana cara agar pengambilan menjadi asinkron di klien, tetapi menyinkronkan di server, tanpa memiliki dua API yang berbeda?

kembali: 'B'. Pada dasarnya masalah yang sama seperti di atas. Apakah orang harus membuat sedikit ketergantungan JSON untuk mengikuti setiap rute mereka?

Apakah ada solusi jangka pendek lainnya sementara kami menunggu solusi yang didukung React? Adakah garpu atau plugin pengguna? https://www.npmjs.com/package/react-async ?

@NickStefan

Saya tidak mengerti masalahnya. :-(

  1. Gunakan wadah Flux atau mirip Flux (Alt, Redux, Flummox, dll) di mana toko tidak lajang.
  2. Tentukan metode statis seperti fetchData pada penangan rute Anda yang mengembalikan janji.
  3. Di server, cocokkan rute ke komponen, ambil fetchData darinya dan tunggu sampai selesai sebelum merender. Ini akan mengisi ulang instans toko Flux atau Redux Anda. Perhatikan bahwa store instance bukan singleton—itu terikat pada permintaan tertentu, jadi permintaan tetap terisolasi.
  4. Saat janji sudah siap, render secara sinkron dengan instance toko yang baru saja Anda isi sebelumnya.

Berikut ini tutorial yang bagus untuk Redux yang menjelaskan pendekatan ini: https://medium.com/@bananaoomarang/handcrafting -an-isomorphic-redux-application-with-love-40ada4468af4

@gaearon saya minta maaf jika saya membingungkan Anda. Terimakasih atas responnya. Dari daftar Anda, sepertinya saya benar dalam mengasumsikan solusi untuk ketergantungan data server adalah dengan hanya mendefinisikan kebutuhan data di komponen root Anda (metode statis/artikel yang Anda tautkan). Jika dependensi data Anda ditentukan di root, jauh lebih mudah untuk mengisi toko, dll.

Ini adalah praktik fluks yang baik, tetapi bukankah ini berpotensi membatasi? Jika saya menambahkan komponen kecil di bagian bawah pohon tampilan Anda yang membutuhkan beberapa data yang sangat berbeda, saya kemudian harus mengedit dependensi data di root.

Apa yang saya minta adalah cara untuk komponen yang sangat bersarang untuk menentukan kebutuhan data asinkron.

Jika saya harus menambahkan kebutuhan mereka ke komponen root, bukankah saya menggabungkan root ke kebutuhan satu sub komponen itu?

@NickStefan dengan react-routing , misalnya, pengambilan data async terlihat seperti ini:

import Router from 'react-routing/lib/Router';
import http from './core/http';

const router = new Router(on => {
  on('/products', async () => <ProductList />);
  on('/products/:id', async (state) => {
    const data = await http.get(`/api/products/${state.params.id`);
    return data && <Product {...data} />;
  });
});

await router.dispatch('/products/123', component => React.render(component, document.body));

Solusi tersebut berfungsi tetapi hanya karena semua pengambilan data sebelumnya terikat ke router. Itu bukan masalah dalam banyak kasus (masuk akal, URL menentukan apa yang seharusnya ada di halaman dan oleh karena itu data apa yang dibutuhkan) tetapi secara umum itu adalah batasan. Anda tidak akan pernah dapat membuat komponen mandiri yang dapat digunakan kembali (yaitu aliran Twitter, komentar, kalender) yang akan menangani semuanya sendiri dan yang harus Anda lakukan hanyalah memasukkannya ke dalam hierarki komponen. Satu-satunya cara yang saya temukan yang memungkinkan ini adalah react-async tapi itu cukup banyak hack.

Saya kira komponen React tidak dimaksudkan untuk digunakan dengan cara ini, mereka masih lebih banyak dilihat daripada _controller_-views. Mungkin perpustakaan yang benar-benar baru harus muncul di atas dasar React.

Impian saya adalah suatu hari nanti kita bisa menulis CMS lengkap menggunakan React, seperti Wordpress, Drupal atau Modx. Tetapi alih-alih cuplikan HTML/PHP, kami akan membuat situs web dari komponen React.

@NickStefan

Dari daftar Anda, sepertinya saya benar dalam mengasumsikan solusi untuk ketergantungan data server adalah dengan hanya mendefinisikan kebutuhan data di komponen root Anda (metode statis/artikel yang Anda tautkan). Jika dependensi data Anda ditentukan di root, jauh lebih mudah untuk mengisi toko, dll.

Jika saya harus menambahkan kebutuhan mereka ke komponen root, bukankah saya menggabungkan root ke kebutuhan satu sub komponen itu?

Tidak persis di root—pada level pengendali rute. Mungkin ada banyak penangan rute di aplikasi Anda. Selain itu, perpustakaan seperti React Router mendukung penangan rute bersarang . Ini berarti penangan rute bersarang Anda dapat menentukan ketergantungan, dan kecocokan router akan berisi larik penangan rute hierarkis yang cocok, sehingga Anda dapat Promise.all mereka. Apakah ini masuk akal?

Ini tidak granular seperti "setiap komponen dapat mendeklarasikan ketergantungan" tetapi saya telah menemukan bahwa dalam banyak kasus membatasi pengambilan data ke penangan rute (yang teratas dan bersarang) adalah semua yang saya inginkan. Saya memiliki komponen murni di bawah ini yang menerima data melalui props sehingga mereka bahkan tidak _tahu_ sedang diambil. Masuk akal bahwa mereka tidak dapat memintanya.

Pendekatan "setiap komponen dapat mendeklarasikan ketergantungan" tidak praktis kecuali Anda memiliki semacam solusi kumpulan permintaan. Bayangkan jika <User> menyatakan bahwa itu membutuhkan panggilan API, dan sekarang Anda membuat daftar 100 <User> s—apakah Anda ingin menunggu 100 permintaan? Perincian persyaratan pengambilan data semacam ini hanya berfungsi saat Anda memiliki solusi kumpulan permintaan. Jika itu cocok untuk Anda, Relay adalah persis seperti itu. Tetapi Anda memerlukan backend khusus untuk itu.

@NickStefan Saat ini kami tidak mendukung pengambilan asinkron per komponen. Kami ingin menambahkannya. Masalah ini melacak kemajuan kami, meskipun tidak ada yang secara aktif mengerjakannya dan itu akan membutuhkan restrukturisasi besar sehingga akan memakan waktu cukup lama.

@gaearon

Pendekatan "setiap komponen dapat mendeklarasikan ketergantungan" tidak praktis kecuali Anda memiliki semacam solusi kumpulan permintaan.

Setelah beberapa pemikiran lagi tentang ini, saya setuju dengan Anda. Apa yang saya cari sebenarnya tidak praktis.

Saya awalnya berpikir bahwa bahkan 100 Andacontoh akan baik-baik saja dengan disiplin tim (yaitu hanya membuat <UsersContainer> yang melakukan 1 permintaan untuk semua 100). Tapi itu bukan "skala orang" untuk sekadar memiliki konvensi. Mungkin yang terbaik untuk menegakkan semua dependensi ke root atau router. Bahkan Relay semacam memaksa Anda untuk mendeklarasikan dependensi di Root dengan tiga wadah yang berbeda (RelayContainer, RelayRootContainer, RelayRouter dll). Ini semacam membuktikan poin bahwa satu-satunya cara adalah dengan dasarnya "mengangkat" deklarasi ketergantungan Anda ke atas pohon.

Hai, yang di sana!
Apakah ada pembaruan tentang ini?

+1

Saya berpendapat bahwa jika Anda benar-benar memikirkan hal ini, Anda tidak ingin melakukan ini. Awalnya saya pikir saya ingin React melakukan rendering async. Tetapi orang lain di utas ini meyakinkan saya sebaliknya.

Bahkan di Relay, pada dasarnya Anda harus "mengangkat" dependensi Anda ke atas pohon dengan deklarasi wadah root, wadah relai, dll. Render sinkron adalah cara yang harus dilakukan.

Render asinkron bisa menjadi mimpi buruk. Saya berbicara dari pengalaman bekerja pada basis kode perusahaan dengan tulang punggung yang diretas untuk melakukan pembaruan individual dalam bingkai animasi permintaan.

Sepakat. Dulu saya berpikir bahwa kami membutuhkan ini, tetapi sebenarnya mundur untuk memiliki tampilan yang menentukan data apa yang akan dimuat. Tampilan adalah fungsi dari status aplikasi, yang merupakan fungsi dari permintaan (dan mungkin status pengguna, jika ada sesi). Inilah yang dimaksud dengan React; memungkinkan komponen untuk menentukan data apa yang harus dimuat bertentangan dengan ide "aliran data satu arah", IMO.

sebenarnya mundur untuk memiliki tampilan yang menentukan data apa yang akan dimuat.

Saya tidak sepenuhnya yakin ini benar. Terkadang, komponen adalah satu-satunya yang mengetahui data apa yang akan dimuat. Misalnya, Anda memiliki tampilan pohon yang dapat diperluas yang memungkinkan pengguna menelusuri grafik simpul yang sangat besar - tidak mungkin untuk mengetahui sebelumnya data apa yang perlu dimuat; hanya komponen yang dapat mengetahuinya.

Terlepas dari itu, diskusi ini mungkin menjadi jauh lebih relevan jika kita mengejar ide untuk menjalankan kode React di dalam web worker (#3092), di mana async diperlukan untuk berkomunikasi melintasi jembatan.

Dalam contoh tampilan pohon besar, jika saya benar-benar ingin dapat merendernya dengan jalur yang sudah terbuka di sisi server, maka saya akan menambahkan jalur itu ke struktur URL. Jika ada beberapa komponen kompleks seperti ini, maka saya akan merepresentasikan statusnya dengan parameter GET. Dengan cara ini, komponen tersebut mendapatkan semua manfaat SSR: mereka dapat dirayapi, dapat dinavigasi menggunakan riwayat, pengguna dapat membagikan tautan ke simpul di dalamnya, dll. Sekarang, kami juga memiliki cara bagi server untuk menentukan kebutuhan data apa untuk diambil untuk membuat respon.

perbarui Saya salah mengira grafik sebagai pohon, tetapi saya masih merasa bahwa status pandangan pengguna terhadap grafik itu harus diwakili oleh struktur URL. Juga, tidak menyadari bahwa Anda benar-benar bekerja di React. Jika Anda sedang mengerjakan beberapa kerangka kerja untuk mengintegrasikan lapisan data dengan tampilan secara isomorfik, itu luar biasa. Tapi saya pikir kita bisa setuju bahwa itu melampaui domain tampilan, dan bahwa React tidak boleh mengambil peran sebagai pengontrol tumpukan penuh.

Belum baca threadnya jadi maaf kalo udah pernah dibahas.

Satu hal yang akan sangat membantu adalah jika ada metode react-dom/server yang hanya akan "memulai/membuat instance" komponen dan mengaktifkan metode siklus hidup tetapi biarkan pengembang memutuskan kapan dia siap untuk merender komponen ke string html . Misalnya, pengembang dapat menggunakan setTimeout atau sesuatu yang lebih andal untuk menunggu metode asinkron selesai.

Ini adalah "retas" yang saya gunakan dengan redux saat ini untuk mencapai ini:

  // start/instantiate component
  // fires componentWillMount methods which fetch async data
  ReactDOM.renderToString(rootEle)

  // all my async methods increment a `wait` counter 
  // and decrement it when they resolve
  const unsubscribe = store.subscribe(() => {
    const state = store.getState()
    // as a result, when there are no more pending promises, the wait counter is 0
    if (state.wait === 0) {
      unsubscribe()
      // all the data is now in our redux store
      // so we can render the element synchronously
      const html = ReactDOM.renderToString(rootEle)
      res.send(html)
    }
  })

Masalah dengan pendekatan ini adalah bahwa ReactDOM.renderToString mengaktifkan semua metode componentWillMount lagi yang menghasilkan pengambilan data yang tidak perlu.

Hai teman-teman! apakah ini sesuatu yang sedang dikerjakan? Saya pikir masalah ini semakin penting. Saya baru-baru ini membuat perpustakaan yang memperluas reaksi redux connect ke dataConnect mana Anda juga dapat menentukan persyaratan data penampung. Persyaratan data ini kemudian dibundel runtime dan ditanyakan dengan GraphQL dalam satu permintaan. Saya pikir ini adalah masa depan spesifikasi data karena mempromosikan komposisi dan isolasi dan juga memastikan pengambilan yang lebih berkinerja. (semua konsep terlihat di Relay)

Masalah dengan di atas adalah rendering sisi server. Karena saya tidak dapat menganalisis secara statis persyaratan data mana yang akan saya dapatkan, saat ini, saya perlu merender satu kali untuk mendapatkan bundel untuk meminta dari DB, menyembunyikan toko redux dan kemudian merender ulang untuk mendapatkan string terakhir . Masalahnya mirip dengan @olalonde .

Akan luar biasa untuk dapat memicu tindakan dan pembaruan ke pohon elemen reaksi dan mendapatkan hasil string DOM sesuai permintaan. Begini cara saya menggambarkannya:

const virtualTree = ReactDOM.renderVirtual(rootEle);

// get the bundled query from redux store for example
const bundle = store.getState().bundle;

// Fetch the data according to the bundle
const data = fetchDataSomehow(bundle);

// hydrate store
store.dispatch({type: 'hydrate', data});
// components that should update should be marked on the virtual tree as 'dirty'

virtualTree.update(); // this would only update the components that needed update

const domString = virtualTree.renderToString(); // final result

Pilihan lain adalah membiarkannya memperbarui sesuka hati seperti di sisi klien, memiliki semua kait yang ada dan berfungsi seperti didMount. Tetapi alih-alih mengubah DOM, itu hanya akan mengubah representasi dom virtual, dan juga merender ke string sesuai permintaan.

apa yang kalian pikirkan? Sesuatu untuk dipertimbangkan atau saya melihatnya sepenuhnya salah?

Halo semua. Saya telah berlangganan masalah ini selama satu tahun atau lebih sekarang. Saat itu, saya berpikir bahwa saya harus dapat menentukan data yang harus dimuat dari dalam komponen, karena komponen adalah unit modularitas utama saya. Masalah ini sangat penting bagi saya, dan saya pikir itu pasti akan diselesaikan di versi React berikutnya.

Sejak itu, saya telah mengembangkan rasa hormat yang jauh lebih dalam terhadap cita-cita di balik REST/HATEOS, karena kesederhanaan skala besar yang muncul dalam sistem aplikasi (browser, crawler, server aplikasi, proxy, CDN, dll) ketika prinsip panduannya adalah diikuti. Terkait dengan masalah ini, _URL harus menjadi satu-satunya representasi sebenarnya dari status aplikasi_. Itu, dan hanya itu, harus menentukan informasi apa yang diperlukan untuk melayani permintaan. Lapisan tampilan, Bereaksi, seharusnya tidak menentukan ini; itu harus dibangun berdasarkan data. Tampilan adalah fungsi dari data, dan data adalah fungsi dari URL .

Saya ragu-ragu untuk membuang ini di masa lalu, karena saya terus mendengar bahwa ini adalah ide yang bagus, tetapi dunia nyata terlalu rumit untuk ini bekerja. Dan terkadang, saya mendengar contoh yang membuat saya mundur selangkah, dan bertanya-tanya apakah saya terlalu bertele-tele, atau terlalu idealis. Tetapi ketika saya merenungkan contoh-contoh ini, saya pasti menemukan cara yang masuk akal untuk mewakili status aplikasi di URL.

  • Apakah negara Anda memiliki parameter yang bervariasi secara independen? Mewakili mereka sebagai parameter kueri terpisah.
  • Apakah negara bagian Anda (atau sebagian) terlalu besar untuk dimasukkan ke dalam URL? Beri nama dan simpan di penyimpanan data yang tidak dapat diubah. Lihat dengan nama / ID.
  • Apakah keadaan Anda berubah begitu cepat sehingga Anda tidak bisa menyimpan semuanya selamanya? Selamat, Anda memiliki data besar yang sah, siap untuk penyimpanan, dan mulai memikirkan hal-hal cerdas untuk dilakukan dengannya. Atau, Anda dapat menghindar, dan dan hanya mengubah data Anda dengan kueri UPDATE, dan menerima bahwa Anda tidak dapat menyimpan semua hal secara permanen nanti (*).
  • Apakah Anda memiliki tampilan yang berbeda untuk pengguna yang berbeda, tetapi disajikan di URL yang sama, misalnya halaman beranda yang dipersonalisasi? Navigasikan setelah identifikasi pengguna ke URL yang menyertakan ID pengguna.
  • Apakah Anda membangun di atas aplikasi yang lebih lama, di mana Anda tidak memiliki opsi untuk memecah struktur URL lama? Itu menyakitkan, saya berada di kapal yang sama. Arahkan ulang struktur URL lama ke struktur URL yang dirancang dengan baik, terjemahkan data sesi (atau apa pun) ke segmen dan parameter jalur URL.
  • Apakah Anda memiliki sedikit atau tanpa kendali atas arsitektur aplikasi Anda? Bukan untuk itu React dirancang, dan kami tidak ingin React dirusak agar sesuai dengan sesuatu seperti arsitektur Wordpress / Magnolia / Umbraco yang saling kompatibel.

Apa yang Anda dapatkan untuk semua pekerjaan itu, yang tidak akan Anda dapatkan jika tidak?

  • Kemampuan satu pengguna untuk membawa pengguna lain ke tempat yang sama dengan mereka di aplikasi, dengan membagikan URL.
  • Kemampuan untuk menavigasi aplikasi menggunakan semua alat yang disediakan browser. Klien standar ada di elemennya.
  • Kemampuan untuk menawarkan pagination yang kompleks dengan cara yang mudah diikuti oleh klien: tautan next dalam tanggapan. API grafik FB adalah contoh yang bagus untuk ini.
  • Grafik terperinci dari alur kerja pengguna Anda di Google Analytics.
  • Opsi untuk membuat grafik tersebut sendiri dari apa pun kecuali log permintaan.
  • Sebuah pelarian dari star-route of chaos: Alih-alih mencocokkan semua permintaan sebagai app.get("*", theOneTrueClientonaserverEntryPoint) , Anda dapat menggunakan kerangka kerja aplikasi server seperti yang dimaksudkan. Anda dapat mengembalikan kode status HTTP yang benar dari permintaan, alih-alih 200 OK\n\n{status: "error"} . Rute bintang menarik, tetapi mengarah ke kegilaan.

Jadi, sekarang React, alat utama kami, tidak mengontrol operasi, bagaimana kami mendapatkan data kami? Bagaimana kita tahu komponen apa yang harus dirender?

  1. Rutekan ke fungsi akses data dan ambil data untuk parameter permintaan yang relevan. Ulangi sampai Anda telah menguraikan seluruh URL dan memiliki objek konteks yang lengkap.
  2. Ubah konteks menjadi objek respons paling sederhana, seolah-olah Anda akan merespons permintaan API. Apakah Anda melayani permintaan API? Kemudian Anda selesai dan KERING.
  3. Lewati objek yang minimal kompleks, tetapi mungkin besar, ke komponen tingkat atas. Dari sini, komposisi komponen React sepenuhnya turun.
  4. Render ke string, tanggapi. Beri tahu CDN Anda bahwa ia dapat menyimpan ini selamanya (* kecuali Anda mengorbankan opsi ini di atas), karena CDN mengidentifikasi berdasarkan URL, dan semua negara bagian Anda memiliki URL. Tentu, CDN tidak memiliki penyimpanan tak terbatas, tetapi mereka memprioritaskan.

Saya tidak bermaksud menjelek-jelekkan di sini, tetapi saya benar-benar merasa bahwa tema inti dari permintaan dalam masalah ini salah arah, dan bahwa React tidak boleh mengimplementasikan apa pun untuk mengakomodasinya, setidaknya bukan karena alasan yang telah saya lihat di sini. tahun lalu. Beberapa aspek arsitektur aplikasi yang baik tidak jelas, dan web sangat sulit untuk diperbaiki. Kita harus belajar dari dan menghormati kebijaksanaan nenek moyang kita, yang membangun Internet dan Web, daripada mengorbankan keanggunan demi kenyamanan.

Itulah yang membuat React hebat: Ini adalah tampilannya. Pemandangan terbaik. Hanya pemandangan. λ.

Harus tidak setuju dengan Anda @ d4goxn. Saya tidak mencoba membuat React lebih dari sekadar lapisan tampilan, dan perutean itu penting tetapi jelas tidak cukup untuk menentukan semua persyaratan data. Dengan menentukan persyaratan data pada tingkat komponen/wadah, Anda tidak membuat React lebih dari sekadar lapisan tampilan. Ini hanya memberi Anda isolasi dan alur kerja yang jauh lebih baik untuk aplikasi Anda. Ini bukan hanya penyederhanaan pekerjaan tetapi kebutuhan untuk aplikasi besar. Bayangkan aplikasi saya memiliki 30 rute dan saya ingin menambahkan komponen profil pengguna di masing-masing rute. Mengikuti alternatif rute di mana semua persyaratan data ditentukan untuk masing-masing, Anda harus melalui 30 rute untuk menambahkan ketergantungan data itu. Ketika dengan jika Anda menentukan kebutuhan data Anda dalam wadah untuk komponen itu, Anda hanya perlu menambahkan komponen di tempat yang Anda inginkan. Ini plug and play. Tidak hanya itu, kueri untuk semua dependensi data komponen dapat dioptimalkan. Relay adalah contoh yang bagus untuk ini, dan pembicaraan tentangnya menjelaskan hal ini jauh lebih baik daripada saya.

Saya sangat menghormati kebijaksanaan lama, tetapi itu seharusnya tidak menjadi batasan untuk evolusi dan menciptakan standar baru, setidaknya begitulah cara saya melihatnya.

Proposal saya pada dasarnya adalah untuk tidak mengubah Bereaksi, tetapi memiliki cara 'hanya virtual' untuk mengubah pohon dom/komponen, pada dasarnya Bereaksi yang dapat 'dijalankan' di sisi server, yang menurut saya cukup mudah dilakukan (cukup blokir tindakan untuk mengubah DOM). Anda masih dapat memiliki caching dan CDN di tempat. Dengan meningkatnya dinamika situs dan aplikasi saat ini, caching statis akan cenderung berkurang tetapi itu topik lain

Jika tampilan menentukan dependensi pada data, maka Anda memerlukan beberapa cara untuk mengoptimalkan grafik dependensi dan mengubahnya menjadi jumlah kueri yang minimal; Anda tidak dapat menyiapkan data sebelum membuat tampilan, meskipun Anda dapat membaginya menjadi dua fase, yang menurut saya rencananya akan ada di utas ini. Ambil kumpulan komponen yang cukup kompleks yang masing-masing akan memiliki kuerinya sendiri, misalnya; mungkin mereka tidak semua instance dari komponen yang sama, dan tidak ada pola yang dapat diciutkan menjadi satu kueri. Saya kira inilah yang ditangani GraphQL. Anda masih perlu menerapkan atau mengintegrasikan server GQL untuk setiap penyimpanan data Anda. Menyortir bagian mana dari kueri GQL yang dioptimalkan yang harus ditangani oleh penyimpanan data mana yang terdengar cukup rumit, tetapi kemudian proposisi saya juga memiliki beberapa kerumitan itu.

Dalam contoh, di mana ada sejumlah besar rute yang semuanya membutuhkan data yang sama, saya sebenarnya tidak akan melihatnya sebagai alasan untuk mengecualikan pengenal untuk sumber data tersebut dari URL. Saya akan memasang modul middleware pengambilan data kecil yang cukup dekat dengan akar tumpukan, yang akan melampirkan objek pengguna itu ke konteks dan kemudian meneruskan konteks ke lebih banyak middleware, dalam perjalanannya ke penangan rute akhir. Komponen root React mungkin tidak mempedulikan bagian tertentu dari konteks itu, tetapi akan meneruskannya ke tingkat anak-anak berikutnya yang mungkin peduli tentang hal itu, atau memiliki keturunan mereka sendiri yang peduli. Jika ini memang memperkenalkan kabel yang terlalu rumit atau dalam, maka sesuatu seperti toko Flux mungkin diperlukan, tetapi itu adalah topik besar tersendiri.

Pemisahan dan isolasi: Saya merasakan sakit Anda di sana. Dalam proposal saya, kami memiliki dua sistem yang sangat berbeda, mengubah data dari formulir yang dioptimalkan untuk penyimpanan dan pengambilan, ke formulir yang dioptimalkan untuk kesederhanaan abstrak. Kemudian pandangan saya mengubahnya menjadi bentuk yang sesuai, karena diturunkan ke hierarki komponen. Menjaga kedua sistem itu secara longgar digabungkan sambil menambahkan fitur yang memerlukan penambahan pada kedua sistem bukanlah apa yang saya sebut sulit, tetapi juga bukan jalur yang paling tidak tahan. Peralihan mental antara pemrograman untuk penyimpanan data dan pemrograman untuk UI dinamis adalah nyata. Kami dulu memiliki pengembang backend dan frontend yang terpisah, dan HTTP berfungsi sebagai antarmuka antara dua paradigma ini. Sekarang saya mencoba menginternalisasikannya ke dalam satu aplikasi, dan Anda mencoba mendelegasikannya.

Saya tidak yakin bahwa peningkatan kompleksitas dan aktivitas dalam aplikasi menghalangi status klien untuk diwakili oleh objek yang sangat kecil, yang mereferensikan kumpulan data yang jauh lebih besar di server. Pertimbangkan penembak orang pertama multipemain besar: ada banyak hal yang terjadi dengan cepat, dan Anda ingin mengirimkan jumlah minimum informasi yang diperlukan dari klien ke host/server game. Seberapa kecil Anda bisa mendapatkannya? Peta semua status input; stempel waktu + rentang ketidakpastian, bidang ~110 bit untuk keyboard, dan beberapa lusin byte untuk mouse / joystick dan orientasi headset VR. Klien akan memprediksi, secara optimis merender dan menghitung, dan server akan memperoleh sejumlah besar informasi dari status kecil dan riwayat status klien, dan mengembalikan sejumlah besar data untuk mengoreksi dan memperbarui semua klien ( semua parameter yang sama untuk semua agen terdekat, dorongan aset), tetapi, aliran permintaan klien itu mungkin masih cocok dengan permintaan 2KB. Dan itu mungkin menjadi keuntungan bagi arsitektur aplikasi.

Saya tidak akan meminta Anda untuk mendidik saya tentang Relay dan GraphQL; Saya hanya menjelajahinya secara dangkal, sebelum API mereka mencapai versi stabil (apakah mereka dikunci sekarang?) Jika Anda masih yakin bahwa menggunakan komponen untuk memilih skema GraphQL, yang menentukan dependensi data, yang menentukan data aktual apa yang perlu diambil, adalah rencana yang bagus, maka mungkin sudah saatnya saya melihat mereka lagi. Saya memiliki beberapa pertanyaan sulit tentang arsitektur itu, tetapi saya akan keluar dari topik.

:bir:

PS Saya tidak bermaksud menyarankan bahwa HTTP akan menjadi protokol komunikasi yang baik untuk MMOFPS

@d4goxn Saya sepenuhnya memahami keraguan dan skeptisisme Anda tentang hal ini. Saya tidak pernah memikirkan hal ini sampai mulai bekerja dengan GraphQL dan kemudian pengenalan konsep Relay. Saya sangat menyarankan Anda untuk melihatnya. Dan menerapkan GraphQL di server baru atau yang sudah ada tidak sesulit yang Anda bayangkan, bahkan jika Anda memiliki banyak penyimpanan data. Tidak ingin keluar topik juga, tetapi ini adalah diskusi penting.

Saya percaya bahwa tingkat abstraksi ini adalah jalan yang harus ditempuh. Saya telah menggunakannya di Relax CMS dan alur kerja serta isolasinya sangat menyenangkan untuk dikerjakan. Tidak hanya itu, data yang diminta tidak lebih dan tidak kurang dari apa yang dibutuhkan ui, dan itu dibuat secara otomatis dengan mengumpulkan data mana yang dibutuhkan setiap komponen dan menggabungkannya. Ini dilakukan oleh Relate http://relax.github.io/relate/how-it-works.html jika Anda tertarik untuk memeriksanya. Dengan ini saya juga dapat memasukkan komponen apa pun di mana saja dalam aplikasi saya dan memastikan bahwa itu akan berfungsi sebagai blok independen dari aplikasi saya.

Satu-satunya hal yang masih harus diketahui adalah rendering sisi server. Setelah melalui kode sumber reaksi, saya yakin mungkin sudah ada solusi seperti yang saya jelaskan, jika tidak, saya dapat mengerjakan solusi jika seseorang dari tim reaksi setuju dengan ini.

@d4goxn Saya juga akan berani berdebat dengan Anda :) Posting asli Anda berisi pernyataan bahwa tampilan adalah fungsi dari data, dan data adalah fungsi dari URL . Saya tidak akan menyebut ini benar-benar mengungkapkan. Ini berlaku cukup banyak untuk situs web atau aplikasi web apa pun yang tidak sepenuhnya acak (ada pertanyaan apakah misalnya keadaan jendela dialog yang terbuka juga harus menjadi bagian dari URL). Karena kita semua memiliki matematika di sekolah, kita tahu bahwa fungsi dapat disusun. Jadi pernyataan sebenarnya mungkin bahwa apa yang Anda lihat di layar adalah fungsi dari URL . Sekali lagi, tidak ada yang benar-benar mengungkapkan, itu hanya formalisasi yang sudah jelas.

Bagi saya, pertanyaan utama adalah bagaimana membangun fungsi seperti itu .

Pendekatan yang Anda sarankan terasa sangat mirip dengan aplikasi MVC sisi server lama yang bagus (mis. Spring MVC adalah contoh yang bagus). URL saat ini mengaktifkan metode pengontrol yang sesuai yang _melakukan logika bisnis_. Ini mengambil semua data yang diperlukan dan meneruskannya ke tampilan. Masalah saya dengan ini adalah sebagai berikut: Ketika Anda melihat halaman web yang dihasilkan, itu semacam hierarki komponen (tidak harus komponen Bereaksi). Yang harus Anda lakukan adalah membuat atau menghasilkan hierarki dari URL. Tapi Anda harus melakukannya dua kali! Pertama, Anda perlu mendekode hierarki di pengontrol untuk mengetahui data apa yang perlu Anda ambil, dan kedua Anda perlu mendekode hierarki tampilan untuk... yah... merender tampilan yang sebenarnya. Saya tidak berpikir itu pendekatan yang sangat _DRY_.

Saya tidak begitu akrab dengan Spring MVC tetapi saya sangat sering menggunakan kerangka kerja MVC lain (kerangka PHP disebut Nette) yang mengatasi masalah ini dengan cara yang sangat mirip dengan bagaimana saya ingin menggunakan Bereaksi. Kerangka kerja ini juga mendukung komponen. Idenya adalah bahwa untuk setiap komponen (misalnya formulir) Anda mendefinisikan pabrik dalam kode Anda yang bertanggung jawab untuk membuat instance komponen dan terutama untuk _memuat semua data yang diperlukan_. Komponen seperti itu kemudian memungkinkan untuk dimasukkan di mana saja dalam tampilan. Jadi saat Anda menulis kode HTML dan membuat _view hierarki_, Anda cukup memasukkan komponen dan pabrik yang mendasarinya menangani inisialisasi yang diperlukan. Komponen berperilaku seperti pengontrol independen kecil, memiliki siklus hidupnya sendiri, menangani dependensinya dan bahkan dapat diparametrikan dari tampilan yang meningkatkan kegunaannya kembali.

Saya telah menggunakan pendekatan ini dengan Bereaksi di sisi klien juga dan tampaknya sangat layak. Komponen React sejak awal dilambangkan sebagai _controller-views_ dan begitulah cara saya menggunakannya. Saya memiliki komponen _controller_ yang bertanggung jawab untuk pengambilan data dan kemudian saya memiliki komponen _view_ yang hanya peduli dengan visual. Seluruh halaman terdiri dari komponen dari kedua jenis. Mereka dipisahkan dengan baik dan mudah digunakan kembali.

Aplikasi saya tidak isomorfik (atau universal seperti yang mereka sebut hari ini) karena saya tidak membutuhkannya tetapi tumpukan yang saya gunakan harus mampu melakukan itu (dan saya percaya bahwa selain dari Relay yang masih eksperimental, tumpukan ini berjalan jalur terpanjang dalam hal ini). FYI, begini tampilannya:

  • Alat inti adalah react-router . Ini memungkinkan untuk menghubungkan permintaan data asinkron pada setiap URL dan berfungsi baik di sisi klien maupun sisi server. Secara formal, pendekatan ini mengalami masalah yang saya sebutkan sebelumnya. Komponen _controller_ itu tidak mungkin. Tapi yang penting di sini adalah desain react-router . Hal ini memungkinkan untuk mendefinisikan _data-hierarchy_ dan _view/hierarki komponen_ di tempat yang sama dan definisi rute yang sebenarnya juga hierarkis. Rasanya sangat _Bereaksi-seperti_.
  • Seluruh status (data) ditangani dengan redux . Selain semua keuntungan lainnya, ia menyimpan seluruh status dalam satu objek yang berarti sangat sederhana dan alami untuk membuat objek di server dan mengirimkannya ke klien. Juga berkat pendekatan connect() , tidak perlu meneruskan semua data dari tingkat atas ke bawah menggunakan alat peraga (itu akan benar-benar gila mengingat ukuran aplikasi saya saat ini).
  • Bagian lain dari teka-teki adalah pemilihan ulang yang memungkinkan kita untuk menjaga keadaan sekecil mungkin. Dan juga sangat meningkatkan faktor decoupling.

Ini masih belum sempurna tetapi ini merupakan kemajuan yang signifikan dari apa yang tersedia ketika saya pertama kali menemukan utas ini. Contoh implementasi dapat ditemukan di sini .

Saya juga memiliki hampir semua isomorfik yang berfungsi, kecuali membutuhkan cara untuk menyelesaikan rendering sisi server ketika pengambilan data berada pada level komponen. Tanpa menambahkan argumen filosofis sekarang, untuk waktu yang lama saya telah berhasil menggunakan reaksi vanilla (ditambah perutean buatan sendiri) dengan komponen mengambil data mereka sendiri di componentDidMount. Saya sangat tidak menyukai gagasan mempertahankan struktur yang berlebihan untuk mendefinisikan komponen dan dependensi data saya - yang sudah ditentukan di pohon komponen saya, dan di sini saya entah bagaimana membutuhkan mungkin beberapa render pass yang menggabungkan data saat diambil secara tidak sinkron, hingga komponen lengkap pohon telah teratasi.

Saat ini, saya hanya ingin menekankan pentingnya hal ini untuk reaksi isomik, imo. Saya akan mencoba mencari solusi sementara itu. Terima kasih.

@decodeman jika Anda tertarik, untuk Relate, saya telah melakukan traverser reaksi minimal untuk mendapatkan dependensi data dari komponen https://github.com/relax/relate/blob/master/lib/server/get-data -dependencies.js. Dengan cara ini saya bisa mendapatkan semua dependensi data -> ambil data -> reaksi render ke string.

@decodeman , FWIW kami telah berhasil menggunakan pendekatan render ganda (seperti yang Anda singgung) di aplikasi produksi besar. Ini terinspirasi oleh contoh redux-saga ini , tetapi pendekatan umum harus bekerja dengan baik dengan semua jenis pemuatan asinkron. Anda hanya perlu memindahkan pemuatan ke componentWillMount . Untuk membantu menjaganya tetap dapat dikelola, kami juga memiliki komponen tingkat tinggi yang menangani skenario pemuatan umum (mis., periksa prop x dan muat jika nil.

Saya juga berpikir akan sangat penting untuk dapat memuat data di dalam komponen dan memiliki semacam metode render malas. Mungkin untuk membuat kedua belah pihak senang, buat metode baru yang disebut asyncRenderToString dan tambahkan acara "siklus hidup" baru yang disebut asyncOnLoad atau semacamnya. Ini hanya akan dipanggil jika Anda memanggil metode asyncRender. Dalam hal duplikasi kode antara server dan sisi klien, ini dapat diperbaiki oleh pengembang dengan hanya memindahkan kode pemuatan umum ke metode terpisah dan memanggil metode itu dari asyncOnLoad dan componentMount . Mendeteksi jika asyncOnLoad selesai mungkin harus menggunakan Janji yang dikembalikan (jika tidak ditentukan dikembalikan/metode tidak ditentukan, ia harus secara langsung memanggil peristiwa siklus hidup yang berlaku dan kemudian merender) jika tidak, ia akan menunggu hingga janji diselesaikan.

Saya pikir solusi seperti itu akan menyelesaikan semua cara peretasan yang harus kita gunakan saat ini untuk rendering sisi server yang tepat. Termasuk misalnya memungkinkan rendering sisi server yang mudah dari aplikasi reaksi dengan router reaksi v4 (yang tidak lagi menggunakan konfigurasi rute terpusat yang saat ini merupakan satu-satunya cara untuk membuat aplikasi isomorfik bekerja)

Ini telah diselesaikan yang disebut aliran periksa aliran reaksi.

Pada hari Kamis, 3 November 2016, Florian Krauthan [email protected]
menulis:

Saya juga berpikir akan sangat penting untuk dapat memuat data di dalam
komponen dan memiliki semacam metode render malas. Mungkin untuk membuat keduanya
pihak senang membuat metode baru yang disebut asyncRenderToString dan menambahkan yang baru
acara "siklus hidup" yang disebut asyncOnLoad atau semacamnya. Ini akan
hanya dipanggil jika Anda memanggil metode asyncRender. Dalam hal kode
duplikasi antara server dan sisi klien ini dapat diperbaiki oleh pengembang
hanya memindahkan kode pemuatan umum ke metode terpisah dan menyebutnya
metode dari asyncOnLoad dan componentMount. Mendeteksi apakah asyncOnLoad adalah
selesai mungkin harus menggunakan Janji yang dikembalikan (jika tidak ditentukan adalah
dikembalikan/metode tidak didefinisikan itu harus langsung memanggil yang berlaku
peristiwa siklus hidup dan kemudian dirender) jika tidak, ia akan menunggu hingga janji
menyelesaikan.

Saya pikir solusi seperti itu akan menyelesaikan semua cara hacky yang harus kita gunakan
saat ini untuk rendering sisi server yang tepat. Termasuk misalnya mengizinkan
rendering sisi server yang mudah dari aplikasi reaksi dengan router reaksi v4
(yang tidak lagi menggunakan konfigurasi rute terpusat yang sekarang
satu-satunya cara agar aplikasi isomorfik berfungsi)


Anda menerima ini karena Anda berlangganan utas ini.
Balas email ini secara langsung, lihat di GitHub
https://github.com/facebook/react/issues/1739#issuecomment -258198533,
atau matikan utasnya
https://github.com/notifications/unsubscribe-auth/ATnWLUIEJw4m1Y3A4oGDOBzP6_ajDcqIks5q6g4_gaJpZM4CHAWq
.

@yamat124 jika saya mencari aliran reaksi, saya hanya menemukan proyek ini: https://github.com/aickin/react-dom-stream

Setidaknya berdasarkan dokumentasi tidak ada cara untuk memiliki peristiwa siklus hidup untuk memuat data sebelumnya yang menunda panggilan render berikutnya untuk subanak sampai Janji diselesaikan. Atau apakah Anda berbicara tentang sesuatu yang lain?

Sekali lagi itu semua cara yang sangat hacky. Ini membutuhkan perutean untuk menjadi independen dari pohon komponen. Dan komponen "Utama" yang termasuk dalam rute harus mengetahui semua data yang perlu dimuat (Berfungsi 99% dari waktu tetapi tidak selalu). Keduanya akan menjadi usang jika reaksi memiliki render komponen yang tertunda.

Saya setuju, ini mungkin hal yang paling saya benci sekarang tentang bereaksi. Benci ketika orang mencoba mempertahankan kerangka kerja agar tidak memiliki fitur ini dengan mengatakan bahwa itu dapat dilakukan secara manual, tentu saja dapat, seperti segala sesuatu yang berkaitan dengan pengembangan perangkat lunak, tetapi itu tidak berarti bahwa itu harus. Fakta bahwa ada banyak modul di luar sana yang memperhatikan apakah hal yang tepat ini harus menunjukkan dengan sendirinya bahwa itu adalah sesuatu yang harus diselesaikan dari dalam kerangka itu sendiri.

Next.js memiliki async getInitialProps untuk melakukan rendering sisi server, sayangnya masih perlu memanggil getInitialProps dari orang tua sampai ke root. Klien Apollo GraphQL juga memungkinkan untuk melintasi semua pohon untuk mengumpulkan persyaratan data sisi server kemudian mengirimkan semuanya kembali. Contoh Berikutnya + Apollo: https://github.com/sedubois/realate.

Akan sangat bagus untuk memiliki metode siklus hidup komponen React async untuk menggabungkan hal-hal ini bersama-sama.

Baru belajar SSR minggu ini, semoga apa yang saya tulis masuk akal

/cc @nkzawa @stubailo

BTW componentWill/DidMount sudah asinkron, apakah itu membantu?

https://twitter.com/Vjeux/status/77202716479139840

@sedubois apakah Bereaksi menunggu janji untuk diselesaikan sebelum merender? Jika tidak, tidak akan membantu!

@olalonde sepertinya begitu: https://github.com/sedubois/react-async-poc/blob/1d41b6f77e789c4e0e9623ba1c54f5ed8d6b9912/src/App.js

const getMessage = async () => new Promise((resolve) => {
  setTimeout(() => {
    resolve('Got async message!');
  }, 3000);
});
class App extends Component {
  state = {
    message: 'Loading...',
  };
  async componentWillMount() {
    this.setState({ message: await getMessage() });
  }
  render() {
    return (... {this.state.message} ...);
  }
}
loadinggot

@sedubois Perhatikan bahwa, dalam contoh Anda, componentWillMount

  async componentWillMount() {
    this.setState({ message: await getMessage() });
  }

mengembalikan undefined dan sebuah janji, tetapi tangkapan layar menunjukkan bahwa Bereaksi tidak menunggu (tidak bisa) untuk janji, karena itu membuat teks Loading... .

Sunting: Dikoreksi.

@sedubois @polytypic Ini juga akan menyebabkan semua kesalahan yang terjadi di dalam fungsi diabaikan secara diam-diam, karena Janji tidak ditangani sama sekali.

@polytypic @dantman Saya tidak begitu yakin apa yang dimaksud dengan "janji tidak ditangani sama sekali". Ini ditunggu oleh operator await . Anda perlu mencoba-menangkap kesalahan, saya memperbarui contoh (menggunakan memuat/tanda tangan kesalahan mirip dengan apa yang dilakukan Apollo).

    try {
      this.setState({ message: await getMessage() });
    } catch(error) {
      this.setState({ error });
    }

Dan render:

{error ? error : loading ? 'Loading...' : message}
screen shot 2016-11-07 at 13 25 46

@sedubois Ketika Anda async fungsi itu secara implisit mengembalikan janji, throw dalam fungsi secara implisit berubah menjadi penolakan janji itu alih-alih membuang tumpukan. Ini berarti componentWillMount sekarang mengembalikan janji ke pemanggilnya.

Bereaksi tidak melakukan apa pun dengan nilai pengembalian sehingga fungsi Anda sekarang mengembalikan Janji mengharapkan seseorang melakukan sesuatu dengannya, tetapi itu hanya dibuang saja.

Karena tidak ditangani, itu berarti penolakan apa pun terhadap janji itu tidak akan ditangkap oleh kode apa pun di React dan kesalahan apa pun yang tidak ditangani tidak akan muncul di konsol Anda.

Dan bahkan melakukan try..catch tidak akan melindungi Anda dengan sempurna dari ini. Jika setState memunculkan kesalahan dari tangkapan Anda atau Anda salah ketik pada tangkapan, kesalahan itu akan hilang secara diam-diam dan Anda tidak akan menyadari bahwa ada sesuatu yang rusak.

@sedubois Sebenarnya, metode async sepertinya selalu mengembalikan janji dalam JavaScript (well, JavaScript di masa mendatang). Saya bingung memikirkan masalah dengan async-wait di C#. _Maaf atas kebisingannya!_

Namun demikian, tangkapan layar dengan jelas menunjukkan bahwa React tidak menunggu janji untuk diselesaikan. Ia memanggil render segera setelah memanggil componentWillMount dan merender teks Loading... . Kemudian ia memanggil render lagi setelah panggilan ke setState . Jika React benar-benar menunggu janji untuk menyelesaikannya, itu tidak akan membuat teks Loading... sama sekali. Alih-alih, itu akan menunggu hingga janji diselesaikan dan baru kemudian akan memanggil render , yang merupakan jenis perilaku yang dibicarakan masalah ini: untuk dapat melakukan async IO selama rendering sisi server.

@dantman @polytypic terima kasih 👍 Ya, pasti diperlukan perubahan di dalam React untuk menunggu respons dan menangani kesalahan dengan benar. Mungkin menambahkan beberapa await di sana-sini dalam kodenya bisa melakukannya

Secara pribadi saya menggunakan Pekerja Web untuk menyimpan status aplikasi dan memancarkan props pada perubahan (karena saya benar-benar melepaskan penyimpanan data dan mengambil dari komponen React, dan memperlakukan React hanya sebagai penyaji, untuk mempertahankan prinsip aliran searah), jadi itu hanya menunggu sampai pesan dengan persyaratan yang diharapkan (seperti properti tertentu yang benar) untuk dirender.

Dengan begitu, meskipun pesan pertama adalah status "memuat" perantara, pesan tersebut tidak digunakan untuk perenderan sisi server statis, tanpa memerlukan metode komponen asinkron.

@sedubois Itu harus menjadi tumpukan metode yang berbeda; atau perubahan yang merusak. Metode render React saat ini disinkronkan, jadi kami memerlukan metode render server berbeda yang mengembalikan janji.

@dantman Saya tidak ingin memberikan pendapat apa pun di sini karena perairan ini terlalu dalam untuk saya (tujuan awal saya hanya untuk menunjukkan async componentWillMount hack Vjeux ...), tapi hmm, tidak dapat Bereaksi melihat jenis objek dikembalikan oleh metode siklus hidup dan jika itu adalah janji, tangani async dan tangani sinkronisasi (seperti saat ini, yaitu dengan cara yang kompatibel dengan mundur)?

Saya suka apa yang diusulkan @fkrauthan . Metode siklus hidup load yang dapat mengembalikan janji atau undefined dan fungsi tambahan async renderToStringAsync . Tapi load harus selalu dipanggil, di klien dan di server. Jika kita menggunakan render atau renderToString janji yang dikembalikan diabaikan agar sesuai dengan perilaku yang kita miliki saat ini. Jika kita menggunakan renderToStringAsync dan load mengembalikan janji, janji harus diselesaikan sebelum rendering.

Mengapa tidak menyukai Next.js , tambahkan fungsi daur hidup React async getInitialProps yang dipanggil sebelum konstruktor? Jika tidak ada metode seperti itu, panggil konstruktor secara langsung.

const props = await (Component.getInitialProps ? Component.getInitialProps(ctx) : {});
...
const app = createElement(App, {
  Component,
  props,
  ...
});

Sepertinya Next.js berhasil melakukan pekerjaan itu, kecuali bahwa getInitialProps nya tidak terhubung ke siklus hidup komponen React sehingga hanya dapat dipanggil pada komponen tingkat atas. Jadi ide-idenya ada dan bisa digabungkan begitu saja?

@sedubois Saya tidak berpikir getInitialProps adalah tempat yang benar. Pertama-tama orang yang menggunakan ES6 mereka tidak memiliki/menggunakan metode itu. Kedua dari semua Anda tidak ingin bekerja pada initalProps (itu hanya default) Anda ingin bekerja di atas gabungan initalProps + diteruskan di props. Oleh karena itu saran saya tentang acara siklus hidup baru yang dikirim sebelum componentWillMount .

Saya pikir ide @Lenne231 dapat menyebabkan beberapa masalah karena orang mungkin menyampaikan di sana peristiwa siklus hidup lain agar janji itu sudah diselesaikan. Memiliki dua perilaku berbeda dari peristiwa siklus hidup yang sama membuatnya sedikit rumit.

Oleh karena itu harus ada di server dan di klien satu metode render sinkronisasi yang tidak memanggil siklus hidup baru itu sama sekali. Dan versi async yang memanggil siklus hidup itu dan selalu menunggu hingga janji diselesaikan sebelum melanjutkan. Itu menurut saya akan memberi Anda fleksibilitas terbesar karena Anda sekarang dapat memutuskan apakah Anda memerlukan siklus hidup itu pada render klien Anda juga atau hanya untuk render server Anda.

orang yang menggunakan ES6 mereka tidak memiliki/menggunakan metode itu

@fkrauthan mungkin Anda merujuk ke getInitialState ? Di sini saya berbicara tentang yang baru, getInitialProps (nama yang digunakan di Next.js).

Adapun poin kedua Anda, ya mungkin lebih baik memiliki metode siklus hidup baru sebelum componentWillMount daripada sebelum constructor . Saya kira tidak seperti itu di Next karena mereka tidak memiliki kemewahan untuk mengubah siklus hidup React seperti yang disebutkan sebelumnya. Karenanya mengapa akan sangat bagus untuk membawa ide-ide itu ke dalam React.

@sedubois Bahkan yang itu. Saya menggunakan misalnya fitur es7 dan mendefinisikan static props = {}; Di badan kelas saya. Itu membuat kode lebih enak dibaca menurut pendapat saya dan saya yakin semakin banyak orang akan beralih ke kode itu segera setelah es7 resmi dirilis.

Catatan: properti kelas bukan fitur "ES7" karena ES7 telah diselesaikan dan satu-satunya fitur bahasa baru adalah operator eksponensial. Yang Anda maksud adalah proposal properti kelas tahap 2 . Itu belum menjadi bagian dari bahasa dan mungkin berubah atau dijatuhkan.

Saya tidak melihat perlunya fungsi async dalam reaksi untuk mengambil data, sebagai gantinya saya akan senang dengan perender virtual yang akan renderToString sesuai permintaan. Jadi Anda dapat mengatur tindakan dan pohon virtual akan memperbarui sesuai sebelum merender ke string. Ini saya pikir akan ideal untuk mencapai SSR yang baik dalam reaksi. Meninggalkan pengembang bagaimana dan di mana mengambil data.

Saya telah menyebutkan sebelumnya tetapi api yang baik akan menjadi sesuatu seperti berikut (dengan sedikit pembaruan dengan apa yang saya sarankan sebelumnya):

const virtualTree = ReactDOM.renderVirtual(rootEle);

// get the bundled query from redux store for example
const bundle = store.getState().bundle;

// Fetch the data according to the bundle
const data = await fetchDataSomehow(bundle);

// hydrate store (this will set updates on the virtual tree)
store.dispatch({type: 'hydrate', data});

// final result
const domString = virtualTree.renderToString();

Ini akan menghindari masalah yang dialami beberapa klien GraphQL seperti Apollo yang memaksa mereka untuk membuat renderToString ganda. Menjadi yang pertama mengambil dependensi data, dan yang kedua merender dengan data yang terisi. Saya pikir ini cukup membuang-buang pemrosesan karena renderToString cukup mahal.

@ bruno12mota Saya mengerti maksud Anda tetapi sekali lagi hanya jumlah perpustakaan yang mencoba mensimulasikannya (dan dengan solusi Anda masih akan ada ribuan perpustakaan) bagi saya merupakan tanda bahwa mungkin itu harus menjadi fitur inti daripada sesuatu yang Anda harus membangun di atas. Solusi Anda masih membutuhkan lebih banyak panggilan render (virtual tetapi masih membuat panggilan) untuk mendapatkan hasilnya.

@fkrauthan ya tapi render virtual jauh lebih berkinerja daripada harus membuat dua render ke string, yang merupakan masalah utama di sini menurut saya (kinerja di SSR) itu adalah bagian lemah dari React. Ada beberapa percobaan untuk meningkatkan renderToString reaksi tetapi tidak ada kemajuan nyata dalam hal ini oleh tim reaksi (tidak mengkritik di sini, mereka melakukan pekerjaan yang luar biasa).

Saya setuju dengan @bruno12mota , setelah juga membahas masalah ini untuk sementara waktu sekarang, itu adalah pendekatan yang paling sederhana. Ini memberikan kebebasan kepada pengembang untuk menentukan kapan render mereka harus "flush".

Itu membuat segalanya jauh lebih rumit.

1.) Dari sudut pandang kode (saya melihat melalui kode dan membuat render async seharusnya jauh lebih mudah daripada membuat renderer yang hanya menggunakan VDom dan kemudian dapat dibuang pada satu titik)
2.) Sekarang Anda harus mempertimbangkan kembali unmount event juga. Dan itu membuat kode kustom jauh lebih rumit dari yang dibutuhkan. Misalnya memuat data mungkin menghasilkan rendering komponen baru yang juga perlu memuat data. Sekarang tiba-tiba lib perlu mencari tahu komponen mana di posisi mana yang sudah memuat data dan komponen mana yang berisi pemuatan data baru dan hal-hal lainnya.

Saya bersama @bruno12mota. VDom bisa dibilang solusi yang lebih "tingkat rendah" tetapi mungkin akan lebih mudah untuk diterapkan oleh pengembang Bereaksi, tidak akan memiliki masalah kompatibilitas mundur dan dapat memungkinkan komunitas untuk bereksperimen dengan berbagai solusi tingkat yang lebih tinggi.

Misalnya, satu solusi tingkat yang lebih tinggi dapat berupa "komponen tingkat yang lebih tinggi" yang membungkus metode componentWillMount async, menunggu semua janji yang tertunda dan mengaktifkan VDom.renderToString() ketika semuanya telah diselesaikan. Saya menggunakan strategi semacam ini untuk SSR di aplikasi saya meskipun sayangnya saya melakukan banyak pengambilan yang berlebihan sekarang karena tidak ada VDom.

Saya pikir cara terbaik adalah tidak mengubah fungsi apa pun yang kami miliki saat ini. Yang berarti tidak membuat getInitialState, componentWillMount, atau renderToString berperilaku berbeda.

Sebagai gantinya, kita harus menambahkan fungsi baru untuk menyelesaikan masalah yang dibahas di sini. Sama seperti ada fungsi yang hanya dipanggil di klien, mungkin ada fungsi yang hanya dipanggil di server.

Saya pikir kita bisa menambahkan fungsi "renderToStringAsync" ke react-dom/server. Perbedaannya dengan renderToString normal adalah, ia mengembalikan Promise.

Di sisi implementasi, satu-satunya perbedaan adalah bahwa versi async akan, setiap kali membuat instance Komponen, memanggil & menunggu "menginisialisasi()" terlebih dahulu, sebelum memanggil render(). Jika komponen tidak memiliki metode "inisialisasi", komponen tersebut dapat segera memanggil render().

Jadi jika kita memiliki contoh struktur App berikut:

    ComponentB
    ComponentC
        ComponentD
        ComponentE

prosesnya menjadi:

1) instanciate componentA
2) await componentA.initialize();
3) componentA.render()
4) do in parallel(
    instanciate componentB, await componentB.initialize(), componentB.render()
    instanciate componentC, await componentC.initialize(), componentC.render(), do in parallel(
        instanciate componentD, await componentD.initialize(), componentD.render()
        instanciate componentE, await componentE.initialize(), componentE.render()
    )
)
5) render to string

Jadi, pada dasarnya, kita hanya membutuhkan fungsi baru "renderToStringAsync" yang menggunakan fungsi opsional baru "inisialisasi". Ini tidak akan terlalu sulit.

@VanCoding memiliki ide yang sama yang telah saya pikirkan untuk sementara waktu. Saya bertanya-tanya apakah inisialisasi juga dapat dipanggil secara otomatis di componentDidMount pada klien?

Jadi di server itu akan menjadi:

initialize()
render()

Dan pada klien itu akan menjadi:

render() // without data (unless available synchronously)
componentDidMount()
initialize()
render() // with data

Saya memiliki implementasi yang berfungsi dari rendering server reaksi dengan react-router v4, dan react-apollo dengan GraphQL.

itu terinspirasi oleh server-side-rendering-code-splitting-and-hot-reloading-with-react-router .

Pada dasarnya rendering server menggunakan komponen yang disinkronkan, rendering klien menggunakan komponen yang tidak sinkron sehingga webpack async memuat dan mengeksekusi modul javascript.

Omong-omong, rendering server diimplementasikan dengan Nashorn Script Engine (JVM).

Kode rendering klien
https://github.com/shendepu/react-ssr-starter-kit/blob/apollo/src/main.js#L63 -L82

Kode rendering server
https://github.com/shendepu/react-ssr-starter-kit/blob/apollo/src/main.js#L128 -L155

Ada satu tempat penting untuk membedakan komponen yang disinkronkan atau tidak disinkronkan dalam rute.
Komponen yang disinkronkan di Route
https://github.com/shendepu/react-ssr-starter-kit/blob/apollo/src/routes/Counter/Route.js#L10

Komponen yang tidak sinkron di RouteAsync
https://github.com/shendepu/react-ssr-starter-kit/blob/apollo/src/routes/Counter/RouteAsync.js#L7 -L23

CATATAN: nama file HARUS Route.js atau RouteAsync.js. Kode harus selalu mengimpor RouteAsync.js. Webpack akan menggantinya dengan Route.js jika dibuat untuk rendering server.
https://github.com/shendepu/react-ssr-starter-kit/blob/apollo/build/webpack.config.js#L72 -L80

Jadi akan ada dua versi build dist dan dist_ssr , dist untuk klien sedangkan dist_ssr untuk server yang hanya memiliki dua bagian: vendor dan aplikasi . Status toko diekspor dan disematkan di <script> dari index.html sebagai __INITIAL_STATE__ . Alasan memiliki dua versi build adalah ReactDomServer.renderToString() belum mendukung komponen async.

Satu-satunya masalah dengan pendekatan two-versions-build adalah bahwa ada peringatan dalam mode dev:
React attempted to reuse markup in a container but the checksum was invalid. This generally means that you are using server rendering and the markup generated on the server was not what the client was expecting. React injected new markup to compensate which works but you have lost many of the benefits of server rendering. Instead, figure out why the markup being generated is different on the client or server:

Tetapi karena satu-satunya perbedaan antara versi klien dan server adalah Route.js dan RouteAsync.js, sehingga peringatan dapat diabaikan, peringatan tidak ditampilkan dalam mode produksi.

Untuk pengambilan data async sebelum rendering komponen di sisi server:

  • Rest API: Menambahkan loadData statis dalam penggunaan komponen
const { matchedRoutes, params } = matchRoutesToLocation(rootRoute.routes, 
                                                        location, [], {}, rootRoute.pattern)
matchedRoutes.filter(route => route.component.loadData).map(route =>
              route.component.loadData(store, params))

untuk menyelidiki dan mengeksekusi loadData . matchedRoutes adalah larik rute yang cocok yang induknya berasal dari indeks 0 . loadData dapat dieksekusi secara berurutan atau paralel.

  • Kueri GraphQL: cukup gunakan getDataFromTree dari react-apollo/server untuk mengeksekusi semua kueri GraphQL.

BTW: menggunakan Nashorn Script Engine memiliki satu peluang pengoptimalan kinerja: Karena vendor berisi kode perpustakaan dan polyfill yang dijalankan sekali dan digunakan kembali, jadi untuk permintaan selanjutnya, hanya potongan aplikasi yang dieksekusi. Karena perpustakaan dipindahkan ke potongan vendor, aplikasi hanya berisi kode javascript di src yang kecil, sehingga meningkatkan kinerja dibandingkan dengan mengevaluasi potongan vendor setiap saat. (semua javascript dikompilasi sekali dan di-cache, dieksekusi untuk setiap permintaan.)

Saya berbohong bahwa potongan aplikasi hanya berisi kode src. itu juga berisi babel-runtime dan duplikat core-js/library direferensikan oleh babel-runtime . babel-runtime tidak dapat dipindahkan ke vendor chunk karena tidak memiliki index.js atau entri utama sehingga webpack tidak dapat mengenalinya sebagai modul. tetapi ukuran kodenya kecil.

Mari kita uraikan lebih lanjut contoh @VanCoding: https://github.com/facebook/react/issues/1739#issuecomment -261577586

Mungkin inisialisasi bukanlah nama yang baik - nama tersebut harus memperjelas bahwa itu dimaksudkan untuk digunakan hanya di server, atau di lingkungan di mana rendering ditunda hingga fungsi ini kembali - alias tidak di klien. Ide: getStaticProps , getServerProps , getInitialProps , getAsyncProps ...

Juga akan baik untuk mendengar dari tim inti apakah ada hambatan teknis atau lainnya untuk mengembangkan metode renderToStringAsync tersebut.

@nmaro Yah, saya pikir fungsinya tidak boleh mengembalikan apa pun, jadi saya lebih suka nama yang tidak dimulai dengan "get", tetapi selain itu, saya masih berpikir ini akan menjadi cara terbaik. Saya tidak keberatan bagaimana itu disebut pada akhirnya.

Selain itu, saya telah melakukan proof of concept untuk this , di sekitar 40 baris kode. Ini tidak dimaksudkan untuk penggunaan produksi, tetapi pengujian saya dengan berbagai komponen berhasil.

Jadi, jika tim reaksi masih tidak menginginkan hal ini terjadi, tidak akan sulit untuk melakukannya di perpustakaan pihak ke-3.

Atau kita bisa menambahkan opsi ke renderToString, seperti pada
renderToString(Component, {async: true})

@nmaro Itu akan membuat tipe pengembalian fungsi bergantung pada opsi yang disediakan, yang menurut saya bukan praktik yang baik. Saya pikir suatu fungsi harus selalu mengembalikan hasil dari tipe yang sama.

@VanCoding apakah Anda memiliki contoh cara menggunakan kode Anda sebagai lib pihak ketiga?

@VanCoding keren! Tapi sejujurnya, saya pikir akan sulit untuk mengintegrasikannya ke dalam algoritma rendering internal reaksi. Lihat https://github.com/facebook/react/blob/master/src/renderers/dom/stack/server/ReactServerRendering.js dan tersesat dalam reaksi internal ... IMO kami benar-benar membutuhkan pendapat dari seseorang dari inti.

@sedubois Anda dapat menggunakan fungsi seperti Anda akan menggunakan fungsi renderToString normal. Tapi itu mengembalikan Janji yang memutuskan ke string, alih-alih mengembalikan string secara langsung.

Jadi menggunakannya akan terlihat seperti:

var react = require("react");
var renderAsync = require("react-render-async");
var MyComponent = require("./MyComponent");

renderAsync(react.createElement(MyComponent,{some:"props"}).then(function(html){
    console.log(html);
});

Secara teori, ia harus dapat merender komponen reaksi yang ada dengan cara yang sama seperti renderToString normal. Satu-satunya perbedaan adalah bahwa komponen async juga didukung.

@nmaro Anda benar. Itu akan sangat membantu.

Apa statusnya dalam hal ini?

@firasdib Kami masih menunggu beberapa

+1

Masalah pengindeksan Google menuntut SSR dan semua bagasi yang dijelaskan di bawah ini adalah untuk kita tangani dan dengan demikian masalah ini sangat penting bagi React untuk berhasil sebagai perpustakaan umum.

Saya pikir seluruh rendering async tidak diperlukan jika JavaScript menyediakan cara yang tepat untuk menunggu janji. Kemudian kita bisa melewati parameter yang menunjukkan cuaca atau tidak kita ingin pemuatan data menjadi sinkron (untuk SSR) atau async (untuk browser web nyata). Logika pemuatan data dapat memulai janji di konstruktor komponen React mana pun dan menunggu dapat dilakukan di componentWillMount.

Namun, seperti yang saya pahami, pengembang React telah menunjukkan keraguan dalam kegunaan componentWillMount dan merekomendasikan untuk membuat konstruktor melakukan semua pekerjaan. Cara apa pun akan berhasil.

Beberapa orang berpendapat bahwa membuat komponen pandangan murni mengatasi masalah tersebut. Mereka sebagian benar. Sayangnya, ini tidak berfungsi dalam kasus paling umum di mana kita tidak tahu data apa yang harus dimuat sampai pohon render selesai.

Inti masalahnya adalah bahwa fungsi render dapat memiliki logika untuk memilih komponen apa yang akan ditambahkan ke DOMtree. Itulah yang membuat React begitu kuat dan sangat tidak cocok untuk rendering sisi server.

IMO solusi sebenarnya adalah dapat mendaftarkan semua janji pemuatan data yang dikeluarkan dengan reaksi. Setelah semua janji pemuatan data selesai, reaksi memanggil kami kembali dengan hasil render. Ini memiliki sejumlah implikasi:

  • DOMtree akan dirender beberapa kali. Ini seperti sistem langsung yang "mengendap" dari status pemuatan data yang tidak stabil menjadi status "dimuat" yang stabil.
  • ada risiko bahwa sistem tidak akan mencapai status "dimuat" yang stabil dalam sistem kereta. Time out harus diperkenalkan untuk menghadapinya.

Hasilnya adalah penggunaan perpustakaan yang jauh lebih "komputasi intens" di sisi server dengan proses yang tidak cepat.

Jadi, saya masih memikirkan solusi yang akan membahayakan kasus umum demi menyelesaikan sesuatu :)

Sementara itu, kami harus menyelesaikan SSR di lingkungan pemuatan data asinkron.

  1. Panggilan pemuatan data harus diturunkan dari URL permintaan (react-router adalah contohnya). Kumpulkan semua janji pemuatan data ke dalam satu daftar janji.
  2. Gunakan Promise.all(), yang dengan sendirinya adalah sebuah janji. Ketika janji ini selesai, berikan data yang dimuat ke fungsi rendering server.

Model ini tidak terikat pada arsitektur desain tertentu. Tampaknya fluks/redux mungkin sedikit mendapat manfaat, tetapi saya tidak yakin karena saya telah meninggalkan model redux di aplikasi saya.

Masalah dalam model ini adalah item #1 di atas. Aplikasi sederhana mengetahui semua panggilan pemuatan data. Dalam aplikasi kompleks dengan "modul" independen, model ini memerlukan semacam replikasi pohon komponen React asli dan bahkan logika render.

Saya tidak melihat bagaimana SSR dapat dibuat untuk bekerja pada proyek yang kompleks tanpa mengulangi logika yang sudah ditulis dalam fungsi render(). Flux mungkin menjadi solusi, tetapi saya tidak berpengalaman di dalamnya untuk memastikan.

Implementasi item #1 tidak jelas. Dalam contoh react-router kita harus sebagai router untuk mengembalikan kepada kita komponen tingkat atas yang termasuk dalam rute itu. Saya pikir kita dapat membuat instance komponen ini:

app.use(function(req, res, next) {
    match({ routes, location: req.url }, (error, redirectLocation, renderProps: any) => {
    if (error) {
        res.status(500).send(error.message)
    } else if (redirectLocation) {
        res.redirect(302, redirectLocation.pathname + redirectLocation.search)
    } else if (renderProps) {
        // You can also check renderProps.components or renderProps.routes for
        // your "not found" component or route respectively, and send a 404 as
        // below, if you're using a catch-all route.
        console.log("renderProps", renderProps)
        for (let eachComp of renderProps.components) {
            // create an instance of component
            // ask component to load its data
            // store data loading promise in a collection
            console.log("eachComp: ", eachComp)
        }
        res.status(200).send(renderToString(<RouterContext {...renderProps} />))
        } else {
        res.status(404).send('Not found')
        }
    })
});

Jadi, komponen tingkat atas bukan lagi komponen "murni". Komponen-komponen ini sekarang memainkan peran pengontrol tingkat atas, karena kemampuan untuk memicu pemuatan data. Model fluks tidak membuatnya lebih baik. Ini hanya memindahkan rute ke asosiasi fungsi pemuatan data ke modul yang berbeda.

Sayangnya, melanjutkan pemuatan data dengan cara ini ke anak-anak pengontrol teratas menghasilkan duplikasi grafik objek yang dibuat untuk tujuan rendering reaksi. Untuk menjaga hal-hal sederhana (jika mungkin) kita harus menempatkan semua pemuatan data ke tingkat pengontrol atas.

Itulah yang saya lihat.

Jika saya menyadari implikasi SSR ini, saya akan berpikir dua kali tentang kegunaan React untuk aplikasi yang dapat diindeks google. Mungkin fluks adalah sebuah solusi, tetapi fluks membuat aplikasi satu tingkat lebih rumit daripada aplikasi React sederhana. Aku pernah disana. Alih-alih fungsi pemuatan data sederhana, saya harus mengejar logika saya di banyak file. Pada tingkat teoretis itu terlihat sangat bagus sampai saya menjalankan proyek. Orang baru mengalami kesulitan untuk memulai. Tidak ada masalah seperti itu setelah menarik redux dari basis kode. Saya pikir itu adalah jawaban untuk semua kompleksitas desain UI :)

Dari React Conf minggu ini, setelah React Fibre (React 16) keluar, mereka berencana untuk mengerjakan hal-hal berikut (seharusnya untuk React 17):

screen shot 2017-03-16 at 15 55 51

Berarti metode siklus hidup React async bisa datang?

Nah, bukankah seratnya kebanyakan tentang rendering ulang yang lebih cepat/halus? Di server, kami hanya merender sekali, jadi saya tidak yakin apakah perubahan itu akan berpengaruh pada rendering sisi server.

@VanCoding memperbarui pesan saya di atas yang agak tidak jelas. Maksud saya rencana mereka setelah fiber keluar.

Untuk orang-orang yang mendarat di sini seperti saya, saya akhirnya membuat implementasi khusus menggunakan reaksi yang mungkin membantu.

https://github.com/siddharthkp/reaqt

kuncinya adalah asyncComponentWillMount yang hanya berjalan di server. itu hanya tersedia untuk komponen titik masuk, tidak semua komponen untuk menghindari masalah yang disebutkan di sini.

@siddharthkp Saya tidak benar-benar melihat apa yang coba dipecahkan oleh komponen Anda? Jika hanya mendukung asyncComponentWillMount untuk komponen React Aplikasi Tingkat Atas, mengapa saya harus menggunakan perpustakaan Anda sama sekali? Saya bisa menyelesaikan janji itu di luar dan kemudian memanggil render? Atau apakah saya melewatkan sesuatu?

@fkrauthan

Saya bisa menyelesaikan janji itu di luar dan kemudian memanggil render?

Anda benar, itulah fungsinya.

Daya tariknya adalah Anda mendapatkan async ssr (+ pemecahan kode + hmr) dengan satu perintah. Saya melihat banyak orang tidak menerapkan ssr karena itu tidak mudah, dan itulah motivasinya.

itu hanya mendukung asyncComponentWillMount untuk komponen React Aplikasi Tingkat Atas

  1. Tidak harus satu komponen aplikasi. Anda dapat mengambil data untuk setiap halaman/layar/titik masuk. (mempromosikan beberapa titik masuk di sini )

  2. Polanya disengaja, jika setiap komponen di pohon menginginkan datanya sendiri, kita mungkin akhirnya mendorong pola kinerja yang buruk. Mengambil data di komponen tingkat atas dan meneruskannya ke anak-anak sebagai alat peraga yang membuatnya tetap terkendali.

@siddharthkp bagaimana repo Anda meningkat dari Next.js (yang juga disajikan di React Conf BTW)?

https://github.com/zeit/next.js

Bagaimanapun diskusi tentang perpustakaan pihak ketiga adalah IMHO di luar topik, yang kami minati adalah dukungan di dalam Bereaksi itu sendiri.

diskusi tentang perpustakaan pihak ketiga adalah IMHO di luar topik, yang kami minati adalah dukungan di dalam Bereaksi itu sendiri.

Saya sangat setuju. Dapat mendiskusikan pertanyaan spesifik dalam masalah reaqt

Sebuah usecase yang saya miliki untuk ini adalah dengan menggunakan react-native dan react-native-vector-icons. Saya ingin penyangga suatu komponen ditentukan oleh data yang diterima dari sebuah janji

const AsyncUser = props => fetchUser()
  .then(data => (
     <User {...data} />
   ))

Jika didukung, saya pikir ini akan membuat banyak kode kompleks lebih mudah dipahami dan mungkin membuka pola async baru.

kami memiliki masalah yang sama, saya ingin melakukan ssr, tidak hanya mencari alat peraga prediksi tetapi juga konten yang diambil oleh tindakan di componentWillMount, bagaimana saya bisa menunggu alat peraga disiapkan, lalu membuat alat peraga menjadi string.

Jika ada yang menganggap ini berguna, saya telah menulis pustaka bernama react-frontload yang memungkinkan Anda mengikat fungsi pengembalian janji ke komponen untuk dijalankan saat komponen dirender, di server dan klien dirender.

Fungsi berjalan 'sinkron' pada render server (tidak juga ;-) tetapi render komponen terakhir diblokir hingga diselesaikan), dan secara asinkron seperti biasa pada render klien, dan tidak berjalan lagi pada klien jika halaman baru saja diberikan server. Ada juga lebih banyak opsi yang dapat Anda tentukan untuk kontrol butir yang lebih halus saat dijalankan, berdasarkan per komponen.

Ini sangat berguna untuk memuat data - cukup banyak kasus penggunaan yang saya tulis untuk dipecahkan. Cobalah! 😄.

https://github.com/davnicwil/react-frontload

<AsyncComponent 
  delayRendering={LoadingComponent}
> 
   {/*return a promise that returns a component here*/}
</AsyncComponent>

Pikirkan semua rendering bersyarat yang tidak perlu Anda lakukan dengan pendekatan di atas karena Anda tahu bahwa Anda memiliki data Anda

Bergabung dalam yang satu ini. Saya pikir itu masih merupakan fitur penting yang tidak dimiliki React. Juga setuju bahwa bahkan jika lib pihak ketiga dapat mencakup subjek ini, ini harus datang langsung dari dalam.

Saya datang dengan implementasi https://github.com/timurtu/react-render-async

Antarmuka komponen async yang sangat bagus, @timurtu.

...Hai semuanya, hanya ingin berpadu di sini karena saya memiliki berbagai hal yang berhubungan langsung dengan utas ini, dan saya telah melacak utas ini untuk waktu yang lama:

  • Pertama, mempertimbangkan SSR, saya pikir pengambilan data tingkat rute paling masuk akal--inilah alasannya:
    Pengambilan data Redux-First Router: memecahkan kasus penggunaan 80% untuk async Middleware
  • rendering server dengan Redux sekarang sangat sederhana, berikut adalah panduan langkah demi langkah dengan Redux-First Router tentang bagaimana melakukannya seperti yang belum pernah Anda lihat sebelumnya: Server-Render seperti Pro /w Redux-First Router dalam 10 langkah
  • terakhir, untuk melakukan pemecahan kode dengan benar, Anda juga harus melakukan keduanya (yaitu keduanya SSR + Pemisahan) . Ternyata secara bersamaan melakukan keduanya telah menjadi titik sakit utama , berhasil dicapai oleh sedikit orang, dan tidak pernah sampai sekarang memiliki paket umum untuk membantu masyarakat. React Universal Component + babel-plugin-universal-import + webpack-flush-chunks (dan extract-css-chunks-webpack-plugin ) adalah keluarga paket yang benar-benar menyelesaikan masalah ini. Dan untuk pertama kalinya. Pada dasarnya, ini memungkinkan Anda untuk merender komponen secara sinkron di server, tetapi yang lebih penting mengangkut potongan yang tepat yang diberikan ke klien untuk render sinkron awal pada klien juga. Anda bahkan mendapatkan stylesheet potongan css melalui plugin webpack. Ini berbeda dari apa yang dibagikan @timurtu dan banyak komponen semacam itu, yang memerlukan pemuatan main.js , penguraian, evaluasi, rendering, dan akhirnya beberapa detik kemudian meminta impor dinamis. Ini adalah solusi untuk mengatasi flash of unstyled content (FOUC), tetapi tidak untuk menyematkan di halaman Anda potongan yang tepat yang diberikan di server. Universal --nama paket keluarga saya--memungkinkan Anda melakukannya dengan waktu yang jauh lebih cepat React Universal Component 2.0 & babel-plugin-universal-import

Saya tahu saya tidak membahas pro/kontra level rute vs level komponen , tetapi tautan pertama berfokus terutama pada itu. Pada dasarnya bermuara pada tingkat komponen yang buruk untuk SSR jika Anda memiliki komponen bersarang yang datanya harus diambil secara berurutan (bukan paralel). Dan jika Anda benar hanya memiliki satu pengambilan untuk dilakukan per rute, Anda sebaiknya memformalkan kontrak dengan melampirkan dependensi data Anda ke entitas rute, daripada melakukannya di componentDidMount . Banyak orang lain di atas sampai pada kesimpulan yang sama. Redux-First Router memformalkan ini dengan sangat baik di sepanjang baris semacam "kontrak" yang membuat Redux begitu menyenangkan untuk digunakan.

Redux-First Router membuat banyak paradigma lama menjadi baru lagi, tetapi secara mengejutkan cocok sebagai bagian yang hilang dari ekosistem Redux. Jika Anda pernah menginginkan router asli untuk alur kerja Redux, cobalah Redux-First Router. Ini relatif baru, dan saya pendatang baru di open source, tetapi ini adalah sesuatu yang telah saya kerjakan selama setahun dan mendapat banyak daya tarik dalam 2 bulan sejak diluncurkan. Orang-orang menyukainya. Saya sangat berharap Anda memeriksanya dan mencobanya :).

Ini juga artikel peluncurannya yang menjelaskan bagaimana ini merupakan solusi yang jauh lebih baik untuk Redux daripada React Router, dan bagaimana ia mendapatkan visi yang benar dengan cara yang sayangnya dilewatkan oleh Redux-Little Router yang juga sangat baik:
Pra Rilis: Redux-First Router — Sebuah Langkah Melampaui Redux-Little-Router

Perbedaan utama antara RLR dan RFR adalah bahwa RFR mengirimkan jenis tindakan yang berbeda per rute (yaitu perubahan URL), daripada selalu hanya LOCATION_CHANGED . Ini memungkinkan Anda mengganti perubahan URL sebagai tipe unik di reduksi Anda, sama seperti tindakan lainnya. Ini mungkin tampak kecil, tetapi membuat perbedaan besar. Karena itu, tidak memerlukan komponen <Route /> , sesuatu yang tidak diperlukan di tanah Redux. Ada juga banyak fitur yang didukung oleh Redux-First Router mulai dari pengambilan data di rute thunks hingga React Native, React Navigation, code splitting, prefetching, dan banyak lagi.

Akan senang mendengar pikiran orang.

@faceyspacey terima kasih, saya pikir ini mengatasi masalah yang saya alami yaitu ketika Anda ingin mengatur alat peraga berdasarkan data async misalnya, react-native-vector-icons dapat mengembalikan ikon sebagai janji. Ini jelas merupakan cara yang berbeda dalam melihat sesuatu tetapi mengurangi componentWillMount atau redux X_GET_START , X_GET_END , dan X_GET_ERROR action boilerplate yang mengikat komponen async ke status, ketika Anda benar-benar hanya ingin membuat permintaan itu satu kali untuk merender komponen. Jika Anda melakukan hal-hal seperti polling data maka stateful dan menggunakan redux mungkin lebih masuk akal

@faceyspacey Saya mengerti apa yang Anda katakan tentang level rute, tetapi bukankah lebih baik memiliki komponen yang lebih kecil yang hanya mengandalkan pengambilan data yang mereka butuhkan sambil merender semua yang lain di sekitarnya secara sinkron?

Lebih baik? Di mana? Di React Native? Di organisasi besar seperti Facebook di aplikasi React Native mereka?

Mungkin di React Native. Ketika ada RSK menurut saya tidak ada perdebatan. Apa yang ingin Anda lakukan tidak mungkin dilakukan tanpa banyak pengambilan per permintaan, kecuali, sekali lagi, Anda membatasinya pada satu komponen yang dapat melakukan pengambilan. Pada titik mana, Anda lebih baik memformalkan kontrak dengan entitas rute.

Adapun React Native dan SPA, tentu saja. Tetapi menguji komponen yang memiliki deps data bawaan sangat merepotkan. Apollo memiliki beberapa hal di sini tetapi terakhir saya memeriksa mereka memiliki daftar tugas yang panjang untuk mendapatkan pengujian komponen yang dipasangkan data dengan benar, yaitu mulus. Saya lebih suka pengaturan di mana Anda memiliki satu toko yang mudah diatur dan diejek. Anda dapat menggunakan kembali bagaimana Anda melakukannya di semua pengujian Anda. Kemudian dari sana komponen pengujian adalah rendering sinkron sederhana dengan pengambilan snapshot. Setelah Anda menambahkan data deps dalam komponen, pengujian React menjadi kurang intuitif. Tetapi orang-orang melakukannya dan mengotomatiskannya. Ada lebih sedikit standar dan sekarang lebih dekat ke kode ad-hoc khusus. Lebih produktif untuk memiliki toko yang dapat diisi secara asinkron dengan cara yang sangat jelas, daripada memiliki strategi yang berpotensi berbeda untuk setiap komponen asinkron yang perlu diisi.

Saya tidak menentang level komponen jika tidak ada SSR dan Anda memiliki strategi pengujian yang mulus dan menyingkir. Jika Anda adalah mega corp, itu masuk akal karena setiap pengembang tidak perlu menyentuh level rute teratas dan berpotensi merusaknya untuk orang lain. Itu sebabnya FB merintis rute ini dengan Relay dan GraphQL. Faktanya hanya seperti 100 perusahaan di dunia yang benar-benar berada di kapal ini. Jadi saya melihat colocation sebagai lebih hype daripada kenyataan bagi kebanyakan orang/aplikasi/perusahaan. Setelah Anda memahami hal tingkat rute dan memiliki paket yang melakukannya dengan sangat baik seperti Redux-First Router , maka Anda memiliki pendekatan yang jauh lebih mudah untuk mengembangkan tim yang anggotanya diizinkan untuk menyentuh tingkat rute . Lihat saja Demo di beranda kode dan kotak:

https://www.codesandbox.io

Pada dasarnya begitu Anda melihat bagaimana hal itu dilakukan, saya ingin mendengar pendapat Anda. Perlu diingat ini adalah SPA. Jadi, Anda harus meninjau demo repo atau boilerplate untuk contoh SSR. Tapi ini adalah awal yang baik.

Saya pikir cara termudah adalah mendukung metode render async (render mengembalikan janji). Ini akan memungkinkan komponen root menunggu rendering komponen anak-anak dan tentu saja hasil render akan menjadi janji. Saya menerapkan sesuatu seperti ini di preact di sini https://github.com/3axap4eHko/preact-async-example

@faceyspacey Terima kasih atas jawaban terperinci di atas. Saya sangat menyukai ide Redux-First Router, itu pasti menyelesaikan semua masalah saya, dan membuat segalanya jauh lebih transparan dan bersih daripada yang kami miliki sebelumnya.

Terima kasih banyak!

@raRaRa senang Anda menyukainya! ...salah satu tantangan terbesar adalah belum ada abstraksi yang baik untuk pengambilan data tingkat rute sampai sekarang. React React v3 memiliki callback, dan v4 memiliki pola yang sangat baik (terutama dengan paket react-router-config ), tetapi tidak jelas dan bukan kelas satu di v4. Ini adalah pemikiran setelahnya. Lebih penting lagi, itu tidak hidup di Redux. Jadi baru sekarang untuk pertama kalinya pengguna redux memiliki abstraksi "tingkat rute" yang lengkap.

Saya pikir sekarang dengan RFR, kita akan melihat pemikiran ulang tentang pentingnya "tingkat rute" vs "tingkat komponen." Hal-hal seperti Relay dan Apollo telah menciptakan banyak hype seputar memasangkan dependensi data ke komponen, tetapi jika Anda tidak menggunakan 2 itu, Anda mungkin akan berakhir di dunia yang terluka di beberapa titik dengan melakukan hal-hal di penangan siklus hidup .

Yang mengatakan pada akhirnya RFR akan memiliki integrasi tingkat rute dengan Apollo. Hanya ada sedikit manfaat untuk melakukan sesuatu di tingkat komponen, dikurangi pengecualian langka yang disebutkan di atas. Setidaknya 80% dari waktu (dan mungkin 100% di aplikasi Anda), Anda dapat menentukan semua data yang Anda butuhkan berdasarkan rute. Jika tidak memungkinkan, kemungkinan karena Anda mengambil data di "lapisan tampilan" yang dapat dianggap ulang untuk semua ditafsirkan oleh URL. Jadi, bahkan dengan alat seperti Apollo, Anda mulai terlibat dalam anti-pola bermasalah yang kemungkinan besar pada akhirnya akan menyebabkan Anda sakit dengan membiarkan "keadaan" hidup di tempat lain selain lapisan tampilan. Yaitu keadaan yang diperlukan untuk melakukan pengambilan data tersebut. Anda sering kali harus menyaring parameter yang tepat yang digunakan untuk mengambil data Anda. Jadi, Anda mengambil status dan props dan mengubah 2 untuk mendapatkan params sesaat sebelum mengambil data. Itu adalah hal-hal yang sekarang hidup di lapisan tampilan dan tunduk pada mekanisme rendering ulangnya (yaitu ketika penangan siklus hidup dipanggil). Setiap aplikasi pada satu titik atau lainnya berakhir dengan data yang tidak diambil saat seharusnya atau diambil sebagai respons terhadap props yang tidak terkait yang diterima/diubah. Dan di semua aplikasi, Anda harus menulis kode tambahan untuk melindungi dari properti yang tidak terkait yang telah berubah. Yaitu alat peraga yang tidak memengaruhi params yang digunakan untuk mengambil data.

Sedangkan untuk React Native + Apollo, masalahnya lebih sedikit karena Anda tidak memiliki SSR dan oleh karena itu potensi pengambilan data berurutan terjadi di server. Namun, apakah Anda benar-benar membutuhkan pengambilan data tingkat komponen saat layar sangat kecil. Anda biasanya tahu apa yang dibutuhkan setiap "adegan" dalam hal datanya. Ini tidak seperti dasbor panel web desktop dengan sejuta grafik, banyak tabel, dll. Satu kasus penggunaan itu mungkin merupakan tempat utama terbesar di mana tingkat komponen mulai masuk akal. Tetapi bahkan di sana Anda dapat menentukan semua persyaratan Anda untuk semua widget itu di satu tempat dan mengambilnya melalui Apollo. Saya belum cukup memikirkannya, tetapi ide dasarnya adalah untuk melampirkan persyaratan data GraphQL Anda ke rute, dan kemudian di komponen Anda terhubung ke data seperti data Redux lainnya. Jadi lebih khusus lagi, idenya adalah untuk menghilangkan persamaan connect reaksi-redux dan Apollo. Saya hanya ingin satu, dan kami ingin itu lebih datar. Menggunakan Apollo membutuhkan destrukturisasi data yang lebih dalam untuk mendapatkan akses ke apa yang benar-benar Anda inginkan dalam komponen Anda. Visi adalah salah satu cara yang terlihat seperti Redux biasa, ditambah spesifikasi graphql di tingkat rute.

Agak terlalu dini untuk membicarakannya, karena saya baru saja melihatnya, tetapi jika level rute adalah pendekatan yang tepat untuk aplikasi Anda, Apollo + GraphQL tidak boleh menjadi pengecualian.

Terakhir, pengujian menjadi jauh lebih mudah jika semua dependensi data dan pekerjaan asinkron Anda terpisah dari komponen. Pemisahan masalah adalah pendorong produktivitas utama ketika Anda dapat menguji komponen Anda tanpa harus khawatir tentang pengambilan data. Tentu saja dalam pengujian Anda, Anda harus mengisi toko Anda dengan melakukan pengambilan asinkron sebelumnya, tetapi dengan memisahkannya dari komponen Anda, Anda dapat dengan mudah memformalkan pekerjaan tersebut di bawah beberapa fungsi pembantu setup digunakan semua pengujian Anda untuk pra- isi toko sebelum menguji bagaimana komponen dirender :)

@faceyspacey Saya sepenuhnya setuju. Tetapi saya harus mengakui bahwa saya belum mencoba Apollo atau GraphQL, saya pasti akan memeriksanya dan melihat bagaimana mereka cocok dengan kasus penggunaan saya.

Saya sangat terbiasa menulis aplikasi web menggunakan pola MVC, di mana pada dasarnya saya menarik semua data dan dependensi pada pengontrol. Kemudian saya menyuntikkan data ke pandangan atau logika bisnis. Seperti yang Anda katakan, itu membuat pengujian lebih mudah karena tampilan tidak digabungkan dengan pengambilan data.

Saya telah melihat aplikasi MVC di mana bagian View mengambil data dan melakukan logika bisnis, dan itu mengerikan. Keduanya saya tidak tahu data apa yang akan diambil karena disembunyikan di beberapa tampilan/komponen dan pengujian menjadi lebih sulit/tidak mungkin.

Bagi saya, Redux-First Router sangat mirip dengan bagian Controller di MVC, ditambah dengan routenya. Yang sangat masuk akal :)

Terima kasih.

Pada akhirnya, tumpukan baru kami adalah ini:

M: Redux
V: Bereaksi
C: Redux-First Router (segera diganti namanya menjadi "Rudy" )

Ketika React keluar (dan khususnya Redux), kami semua tampaknya ingin membuang kearifan kuno MVC, seolah-olah itu berarti kami dapat sepenuhnya membersihkan diri dari kesulitan dalam mengembangkan aplikasi sebelumnya. Tapi saya pikir pada akhirnya itu tidak lebih dari sekadar tidak memiliki alat yang dibangun untuk mendukungnya. Pada akhirnya, kami masih mendapat banyak manfaat dari MVC, bahkan dalam aplikasi yang mengutamakan klien yang reaktif.

Ini mungkin bekerja sedikit berbeda di tumpukan pertama klien baru vs. MVC sisi server tradisional, tetapi pada dasarnya itu adalah 3 pemisahan masalah yang sama.

Sejauh apa perbedaan dalam interaksi/perilaku antara 3, pada dasarnya di masa lalu pengontrol akan mendapatkan model dalam fase awal dan kemudian membuat tampilan dengan mereka; dan sekarang pengontrol (RFR) segera merender tampilan (dipilih melalui "model UI," yaitu status Redux), dan kemudian merender ulang tampilan untuk kedua kalinya setelah pengontrol menemukan model domain (juga disimpan dalam status Redux).

Halo teman-teman, tonton tautan ini. di sini adalah contoh rendering sisi server data https://github.com/bananaoomarang/isomorphic-redux

@harut55555 idk mungkin hanya saya tetapi ini sepertinya banyak kode untuk melakukan permintaan dapatkan dan permintaan posting

Ada perubahan? Melepaskan 16 reaksi dan sebagian besar solusi tidak berfungsi

Saya pikir pendekatan saat ini menggunakan redux dengan perkakas seperti redux observable atau redux thunk

Kasus penggunaan saya

<App>
    <Page>
        <AsyncModule hre="different.com/Button.react.js" /> downloaded from external url on server or client
    </Page>
</App>

Masalahnya adalah saya tidak tahu sebelum merender Aplikasi komponen seperti apa yang akan saya miliki

Mengapa tidak mengunduh data/konten alih-alih komponen hanya ingin tahu?

Kapan? Saya memiliki halaman dinamis dan komponennya mungkin atau mungkin tidak

Sepertinya Anda ingin rendering bersyarat https://reactjs.org/docs/conditional-rendering.html

Saya pikir ini masalah perutean, reaksi seharusnya hanya membuat halaman, bukan mengelola dan meminta data render

Menggunakan @graphql dari Apollo membuat pemuatan data menjadi sangat sederhana, dan API berbasis komponen React-Router v4 membuat perutean yang dapat dikomposisi menjadi sederhana. Keduanya ortogonal dengan status aplikasi di Redux.

Untuk melakukan rendering streaming single-pass yang menunggu pengambilan data, render() seharusnya mungkin untuk mengembalikan janji (pada klien yang Anda lakukan seperti sekarang, hanya di server Anda mengembalikan janji).

Kemudian @graphql dapat dengan mudah mengembalikan janji untuk render() setelah data dimuat; semua sisanya hanyalah Bereaksi biasa.

Beberapa bulan yang lalu saya memberikan ceramah di JSConf Islandia yang menjelaskan fitur rendering async yang akan datang di React (lihat bagian kedua): https://reactjs.org/blog/2018/03/01/sneak-peek-beyond-react -16.html. Ini tentang pengambilan data sisi klien.

Sekarang @acdlite memberikan ceramah tentang bagaimana konsep yang sama dapat diterapkan untuk mengaktifkan menunggu data secara asinkron di server dalam komponen React, dan secara bertahap menghapus markup saat sudah siap: https://www.youtube.com/watch?v= z-6JC0_cons

Harap Anda akan menikmati menonton pembicaraan ini! Saya pikir dalam satu tahun atau lebih kami mungkin dapat menyelesaikan masalah ini dan memiliki strategi resmi untuk ini.

Aku ingat cincin. Kami menikah merencanakan masa depan bersama. Tolong akhiri ini sekarang dan temui aku. Waktu bekerja selalu cinta

Dimungkinkan untuk menggunakan ReactDOMServer.renderToStaticMarkup untuk berjalan di pohon, menempatkan bookmark di mana ia mencapai dependensi asinkron (biasanya memuat data dari API), dan terus berjalan di pohon saat dependensi tersebut diselesaikan, hingga seluruh pohon diselesaikan, dan kemudian lakukan ReactDOMServer.renderToString final untuk benar-benar merender ke HTML, yang akan disinkronkan karena semua dependensi sekarang telah diselesaikan.

Saya telah mengambil pendekatan ini di react-baconjs : render-to-html.js . Itu terinspirasi oleh komentar @gaearon yang dibuat di edisi lain yang merekomendasikan pendekatan semacam itu.

@steve-taylor ya, ini berhasil. Ini juga solusi yang saya gunakan di react-frontload yang merupakan solusi tujuan yang lebih umum untuk ini yang akan berfungsi di aplikasi React apa pun.

Seperti yang Anda katakan pada dasarnya itu hanya memalsukan rendering asinkron dengan menjalankan logika rendering sinkron dua kali dan menunggu semua janji pemuatan data yang Anda tekan pada render pertama untuk diselesaikan sebelum Anda menjalankan yang kedua.

Ini bekerja cukup baik meskipun jelas sedikit boros (output dari render pertama lebih dari sekadar janji, itu juga HTML sebenarnya yang dibuang begitu saja). Akan luar biasa ketika rendering server async yang sebenarnya membuatnya menjadi React.

@davnicwil kerja bagus! Saya telah berpikir untuk menggeneralisasi solusi SSR async spesifik. Apakah react-frontload menangani kedalaman async yang tidak terbatas? Bertanya karena Anda mengatakan "dua kali".

@steve-taylor bersorak! Yap, jika kedalaman yang Anda maksud adalah kedalaman komponen di pohon, itu tidak terbatas. Ini memungkinkan Anda mendeklarasikan fungsi pemuatan data pada Komponen itu sendiri (dengan Komponen Orde Tinggi) dan kemudian ketika itu ditemui pada render pertama itu dijalankan dan janji dikumpulkan.

Apa yang tidak akan berfungsi adalah ketika ada komponen turunan lebih lanjut yang juga memuat data secara asinkron yang kemudian akan dirender secara dinamis tergantung pada hasilnya, jika itu masuk akal. Itu hanya berarti aplikasi harus terstruktur sehingga tidak ada sarang semacam ini, yang saya temukan dalam praktiknya sebenarnya bukan batasan besar. Sebenarnya dalam banyak hal lebih baik dari segi UX karena tentu saja logika asinkron bersarang berarti permintaan serial, dan waktu tunggu yang lebih lama, sedangkan perataan berarti permintaan paralel.

Yang mengatakan masalah bersarang (memang mungkin seluruh masalah rendering server async ini) dapat diselesaikan dengan hal-hal Suspense yang masuk ke React dalam waktu dekat. 🤞.

FYI kami sudah mulai mengerjakan ini.

https://reactjs.org/blog/2018/11/27/react-16-roadmap.html#suspense -for-server-rendering

Luar biasa @gaearon!

@davnicwil react-baconjs mendukung kedalaman tak terbatas.

@gaearon Saya ingin tahu apakah ini akan memungkinkan dukungan untuk observables di create-subscription, sehingga saya dapat mengubah JSX yang dapat diamati menjadi komponen reaksi?

Saya ingin memiliki fitur ini karena dua alasan. Pertama, di aplikasi saya, status disimpan dalam pekerja web. Jadi saat mengambil bit dari status yang diperlukan oleh komponen adalah operasi asinkron, dibutuhkan waktu seperti 5 ms dan tidak masuk akal bagi React untuk merender apa pun sambil menunggu data. Kedua, saat ini tidak ada cara universal untuk mengonversi yang dapat diamati menjadi komponen: jika yang dapat diamati memancarkan nilai pertama secara serempak, maka yang Anda lakukan adalah berlangganan untuk mendapatkan nilai itu, lalu berhenti berlangganan, lalu berlangganan lagi untuk mendengarkan perubahan (itulah Replay contoh subjek dalam dokumen yang dapat diamati). Dan jika itu memancarkan nilai pertama secara tidak sinkron, maka Anda awalnya membuat nol dan mendengarkan perubahan.

@steve-taylor react-frontload sekarang juga mendukung kedalaman tak terbatas dari penyarangan komponen, dengan rilis 1.0.7 .

Semoga perpustakaan ini bermanfaat bagi sebagian orang yang membuka utas ini - jika Anda mencari solusi pemuatan data asinkron yang berfungsi pada render klien/server, dengan upaya integrasi yang sangat minimal, Anda harus memeriksanya.

Kami telah mengalami masalah ini sebelumnya karena aplikasi kami dibuat dengan React Hooks, dan kemudian kami membuat paket react-use-api untuk menyelesaikannya, yang merupakan kait khusus yang mengambil data API dan mendukung SSR. Semoga paket tersebut dapat membantu masyarakat yang membutuhkan.

Namun, kami masih menantikan untuk menunggu solusi resmi, Sama seperti yang dikatakan react-frontload , tidak ada cara bawaan untuk menunggu pemuatan data asinkron terjadi setelah render dimulai saat ini.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat