Redux: Teknik pemuatan sisi server async terbaik?

Dibuat pada 15 Jun 2015  ·  63Komentar  ·  Sumber: reduxjs/redux

Pertama-tama, saya suka perpustakaan ini dan pola yang kalian gunakan. 👍👍

Saya mencoba menggunakan redux untuk membangun aplikasi isomorfik. Sejauh ini berfungsi dengan baik, kecuali bahwa saya perlu mencari cara untuk menunggu hingga toko saya dimuat (di server) sebelum mengembalikan pemuatan halaman awal. Idealnya, pemuatan harus dilakukan di toko itu sendiri, tetapi ketika saya memanggil dispatch(userActions.load()) , toko harus mengembalikan status baru (yaitu return { ...state, loading: true }; ), sehingga tidak dapat mengembalikan janji ke tunggu. dispatch() mengembalikan tindakan yang diteruskan ke sana untuk beberapa alasan. Saya sangat ingin sesuatu seperti...

dispatch(someAsyncAction, successAction, failureAction) => Promise

...di mana janji tidak diselesaikan sampai salah satu dari dua tindakan lainnya dikirim.

Apakah itu hal yang dapat diaktifkan dengan pola middleware?

Apakah saya benar-benar keluar dari basis dan sudah ada cara sederhana untuk melakukan ini?

Terima kasih.

Komentar yang paling membantu

// Middleware
export default function promiseMiddleware() {
  return (next) => (action) => {
    const { promise, types, ...rest } = action;
    if (!promise) {
      return next(action);
    }

    const [REQUEST, SUCCESS, FAILURE] = types;
    next({ ...rest, type: REQUEST });
    return promise.then(
      (result) => next({ ...rest, result, type: SUCCESS }),
      (error) => next({ ...rest, error, type: FAILURE })
    );
  };
}

// Usage
function doSomethingAsync(userId) {
  return {
    types: [SOMETHING_REQUEST, SOMETHING_SUCCESS, SOMETHING_FAILURE],
    promise: requestSomething(userId),
    userId
  };
}

Semua 63 komentar

Hei, terima kasih!

Idealnya, pemuatan harus dilakukan di toko itu sendiri

Redux memberlakukan bahwa Toko benar-benar sinkron. Apa yang Anda gambarkan seharusnya terjadi di pembuat tindakan.

Saya _think_ bahkan mungkin dengan middleware thunk default. Pembuat tindakan Anda akan terlihat seperti:

export function doSomethingAsync() {
  return (dispatch) => {
    dispatch({ type: SOMETHING_STARTED });

    return requestSomething().then(
      (result) =>  dispatch({ type: SOMETHING_COMPLETED, result }),
      (error) =>  dispatch({ type: SOMETHING_FAILED, error })
    );
  };
}

dan menangani tindakan aktual (granular) di Store.

Dimungkinkan juga untuk menulis middleware khusus untuk menghapus boilerplate.

Jenius! Saya pikir saya mengabaikan sesuatu yang jelas. Saya suka pemisahan _melakukan_ dan _menyimpan_.

Saya berharap untuk melihat perpustakaan ini berkembang, meskipun sudah cukup lengkap. Semangat, @gaearon!

Anda juga dapat menulis middleware khusus seperti ini

export default function promiseMiddleware() {
  return (next) => (action) => {
    const { promise, ...rest } = action;
    if (!promise) {
      return next(action);
    }

    next({ ...rest, readyState: 'request' );
    return promise.then(
      (result) => next({ ...rest, result, readyState: 'success' }),
      (error) => next({ ...rest, error, readyState: 'failure' })
    );
  };
}

dan gunakan itu alih-alih yang default.

Ini akan memungkinkan Anda menulis pembuat tindakan asinkron seperti

function doSomethingAsync(userId) {
  return {
    type: SOMETHING,
    promise: requestSomething(userId),
    userId
  };
}

dan minta mereka berubah menjadi

{ type: SOMETHING, userId: 2, readyState: 'request' }
{ type: SOMETHING, userId: 2, readyState: 'success' }
{ type: SOMETHING, userId: 2, readyState: 'failure' }

Ooh, itu bagus juga, dan lebih dari apa yang ada dalam pikiran saya ketika saya mengajukan pertanyaan awal. Saya tidak tahu apakah saya menyukai tradeoff dalam mengurangi jumlah konstanta tindakan dengan imbalan menambahkan if s untuk memeriksa readyState di dalam toko. Saya pikir saya mungkin lebih suka memiliki versi tambahan _SUCCESS dan _FAILURE dari setiap tindakan hanya untuk menghindari menempatkan if di dalam case .

Terimakasih Meskipun.

Ya itu benar-benar terserah selera Anda. Anda dapat memiliki versi serupa yang mengubah types: { request: ..., success: ..., failure: ... } menjadi tindakan. Inilah gunanya menjadikannya sebagai middleware alih-alih memasukkannya ke dalam perpustakaan: setiap orang memiliki seleranya sendiri terhadap hal-hal ini.

// Middleware
export default function promiseMiddleware() {
  return (next) => (action) => {
    const { promise, types, ...rest } = action;
    if (!promise) {
      return next(action);
    }

    const [REQUEST, SUCCESS, FAILURE] = types;
    next({ ...rest, type: REQUEST });
    return promise.then(
      (result) => next({ ...rest, result, type: SUCCESS }),
      (error) => next({ ...rest, error, type: FAILURE })
    );
  };
}

// Usage
function doSomethingAsync(userId) {
  return {
    types: [SOMETHING_REQUEST, SOMETHING_SUCCESS, SOMETHING_FAILURE],
    promise: requestSomething(userId),
    userId
  };
}

Oh man, saya suka solusi itu. Jauh lebih baik daripada memiliki then() dan panggilan tambahan ke dispatch() seperti solusi pertama yang Anda usulkan. Hore untuk middleware!

Beri tahu saya bagaimana (dan apakah ;-) cara kerjanya!
Kami belum benar-benar menguji middleware khusus.

Anda meninggalkan } (itu -1 poin ), tetapi itu berhasil seperti pesona! Pertama kali.

:+1:

@erikras Saya ingin tahu bagaimana Anda menerapkan menunggu janji untuk diselesaikan di server?

Ini hanya pseudocode, jadi jangan tempelkan ini di mana pun, tetapi saya menggunakan react-router (yang apinya berubah secepat redux) sesuatu seperti ini:

app.get('/my-app', (req, res) => {
  Router.run(routes, req.path, (error, initialState) => {
    Promise.all(initialState.components
      .filter(component => component.fetchData) // only components with a static fetchData()
      .map(component => {
        // have each component dispatch load actions that return promises
        return component.fetchData(redux.dispatch);
      })) // Promise.all combines all the promises into one
      .then(() => {
        // now fetchData() has been run on every component in my route, and the
        // promises resolved, so we know the redux state is populated
        res.send(generatePage(redux));
      });
  });
});

Apakah itu menjelaskan sesuatu?

@est

Mengutip masalah Anda di Slack:

Saya punya penangan rute dengan

 static async routerWillRun({dispatch}) {
   return await dispatch(UserActions.fooBar());
 }

di mana UserActions.fooBar() adalah:

export function fooBar() {
 return dispatch => {
   doAsync().then(() => dispatch({type: FOO_BAR}));
 };
}

kemudian di server render saya menghasilkan:

 yield myHandler.routerWillRun({dispatch: redux.dispatch});

tapi itu tidak berhasil.

Saya pikir masalahnya di sini adalah Anda tidak benar-benar mengembalikan apa pun dari metode bersarang fooBar .

Lepaskan kawat gigi:

export function fooBar() {
  return dispatch =>
    doAsync().then(() => dispatch({type: FOO_BAR}));
}

atau tambahkan pernyataan return eksplisit:

export function fooBar() {
  return dispatch => {
    return doAsync().then(() => dispatch({type: FOO_BAR}));
  };
}

Either way, mungkin lebih mudah untuk menggunakan middleware janji kustom seperti yang disarankan di atas.

@erikras Mengenai komentar terakhir Anda di mana Anda memanggil metode fetchData pada apa yang Anda miliki sebagai initialState.components (dalam panggilan balik Router.run), objek yang Anda dapatkan dari referensi komponen hanya mengembalikan penangan rute yang cocok. Apa pendapat Anda tentang mencapai komponen yang mungkin bukan pengendali rute yang cocok, yaitu komponen anak, tetapi perlu mengambil data?

inilah contoh dari apa yang saya bicarakan

import React from 'react';
import Router from 'react-router';
import {Route, RouteHandler, DefaultRoute} from 'react-router';

//imagine Bar needs some data
const Bar = React.createClass({
  render(){
    return(
      <div>bar</div>);
  }
});

const Foo = React.createClass({
  render(){
    return (
      <div>
        foo
        <Bar/>
      </div>);
  }
});


const App = React.createClass({
  render(){
    return (
      <div>
        <RouteHandler />
      </div>
    );
  }
});

const routes = (
  <Route path="/" handler={App} name="App">
    <DefaultRoute handler={Foo} name="Foo"/>
  </Route>
);

Router.run(routes,'/',function(Root,state){
  console.log(state);
});

keluaran:

{ path: '/',
  action: null,
  pathname: '/',
  routes: 
   [ { name: 'App',
       path: '/',
       paramNames: [],
       ignoreScrollBehavior: false,
       isDefault: false,
       isNotFound: false,
       onEnter: undefined,
       onLeave: undefined,
       handler: [Object],
       defaultRoute: [Object],
       childRoutes: [Object] },
     { name: 'Foo',
       path: '/',
       paramNames: [],
       ignoreScrollBehavior: false,
       isDefault: true,
       isNotFound: false,
       onEnter: undefined,
       onLeave: undefined,
       handler: [Object] } ],
  params: {},
  query: {} }

Anda tidak akan memiliki akses ke Bar di Routes

@erikras Fantastis! Itulah jenis rute yang ingin saya lalui. Terima kasih telah berbagi.

@iest Saya harap

@mattybow Itu benar. Jika Anda _benar-benar_ membutuhkan komponen yang tidak ada dalam rute Anda untuk memuat sesuatu, maka satu-satunya pilihan adalah menjalankan React.renderToString() sekali (membuang hasilnya), lakukan semua pemuatan Anda di componentWillMount() , dan entah bagaimana simpan janji saat Anda pergi. Inilah yang saya lakukan dengan solusi perutean buatan sendiri sebelum react-router mendukung rendering sisi server. Saya akan menyarankan bahwa membutuhkan komponen non-rute untuk melakukan pemuatan mungkin merupakan gejala dari masalah desain. Dalam kebanyakan kasus penggunaan, rute mengetahui data apa yang dibutuhkan komponennya.

@erikras
apakah Anda memiliki repo publik untuk melihat seluruh contoh pada solusi Anda di sana?

@transedward Saya berharap saya melakukannya, tetapi barang-barang saya sejauh ini menggunakan metode yang saya rinci di sini masih sangat belum matang. Maaf.

+1 pada contoh isomorfik lanjutan
Saya suka ke mana arahnya!

@transedward Inilah contoh proyek dengan semua teknologi mutakhir yang telah saya buat bersama. https://github.com/erikras/react-redux-universal-hot-example/

@erikras Ini luar biasa! Bisakah Anda mengirimkan PR untuk menambahkannya ke bagian "starter kits" dokumen README dan React Hot Loader ini?

Terima kasih! PR yang diajukan.

@erikras Hebat - Terima kasih!

Sekedar catatan bahwa—berdasarkan beberapa ide dalam percakapan ini—saya membuat middleware untuk menangani janji: https://github.com/pburtchaell/redux-promise-middleware.

@pburtchaell Ada perpustakaan ini oleh @acdlite juga. https://github.com/acdlite/redux-promise

Dua pemikiran tentang ini:

  1. Janji yang dikonversi menjadi tindakan dapat diteruskan bersama tindakan dan dimasukkan ke dalam Redux Store.

Kemudian, untuk mengetahui apakah halaman Anda siap dirender, cukup periksa apakah semua janji telah selesai. Mungkin menggunakan middleware yang mengikat semua janji yang lewat bersama-sama dan memberikan janji ketika tidak ada janji yang tertunda.

  1. Bagaimana dengan membiarkan penyeleksi memanggil tindakan untuk data yang hilang?

Misalkan Anda ingin merender pesan 3, maka wadah pesan Anda akan merender <Message id={3}> dan pemilih pesan akan memeriksa apakah state.msgs[3] ada dan jika tidak, mengirimkan janji pemuatan pesan.

Jadi gabungkan keduanya dan komponen akan secara otomatis memilih data yang dibutuhkan dan Anda akan tahu kapan selesai.

Saya cukup yakin tentang "jangan memasukkan apa pun yang tidak dapat diserialisasikan ke dalam toko atau tindakan". Itu salah satu invarian yang bekerja sangat baik untuk saya (dan memungkinkan perjalanan waktu, misalnya) dan perlu ada _sangat_ alasan kuat untuk mempertimbangkan untuk mengubahnya.

Kemudian, untuk mengetahui apakah halaman Anda siap dirender, cukup periksa apakah semua janji telah selesai. Mungkin menggunakan middleware yang mengikat semua janji yang lewat bersama-sama dan memberikan janji ketika tidak ada janji yang tertunda.

Ini sebenarnya tidak perlu menaruh janji di toko pada akhirnya, dan saya suka ini. Perbedaannya adalah, pada akhir rantai pengiriman, tindakan mentah tidak mengandung janji apa pun. Itu "dikumpulkan" oleh middleware tersebut sebagai gantinya.

Satu hal yang sering saya lakukan ketika bekerja dengan janji adalah menyimpan referensi ke janji sehingga ketika permintaan lain untuk hal yang sama masuk, saya hanya mengembalikan janji yang sama, memberikan debouncing. Saya kemudian menghapus referensi beberapa saat setelah janji selesai, menyediakan caching yang dapat dikonfigurasi.

Saya benar-benar harus mulai menggunakan Redux di aplikasi nyata, karena saya ingin tahu apa yang harus dilakukan dengan referensi tersebut di Redux. Saya agak ingin memasukkannya ke dalam toko untuk membuat actionCreator stateless (atau setidaknya membuat status eksplisit). Melewati janji melalui suatu tindakan adalah cara yang bagus untuk mengekspornya, tetapi kemudian Anda harus mendapatkannya kembali entah bagaimana. Hmmm.

Saya sangat menantikan jawaban untuk poin terakhir dari @wmertens .
Menghindari beberapa panggilan bersamaan (tidak melakukan apa pun jika ada sesuatu yang tertunda) adalah kasus penggunaan yang sering terjadi, tidak yakin apa cara terbaik untuk menjawabnya (karena Anda tidak dapat mengakses status toko dari actionCreator).

Haruskah pengguna actionCreator (komponen) memeriksa setiap kali dalam keadaan apakah dia dapat mengirimkan tindakan atau tidak? Anda harus melakukannya setiap saat.. Mungkin Anda perlu memperkenalkan anotasi @connect Anda sendiri yang membungkus ini?

karena Anda tidak dapat mengakses status toko dari actionCreator

Mengapa? Anda dapat melakukannya dengan baik jika Anda menggunakan middleware thunk .

function doSomething() {
  return { type: 'SOMETHING' };
}

function maybeDoSomething() {
  return function (dispatch, getState) {
    if (getState().isFetching) {
      return;
    }

    dispatch(doSomething());
  };
}

store.dispatch(maybeDoSomething());

yang menyelesaikannya!
Saya berpikir untuk sementara waktu (tanpa alasan) bahwa mengakses status di actionCreator adalah praktik yang buruk ^^ karena pemanggil pembuat tindakan dapat mengakses status, saya tidak mengerti mengapa actionCreator tidak boleh, jadi ya keren kita bisa melakukan ini :)

Terima kasih @gaearon

@gaearon , apakah teknik menggunakan Thunk dan tindakan berbeda ini http://gaearon.github.io/redux/docs/api/applyMiddleware.html lebih disukai daripada jawaban Anda di atas:

Anda juga dapat menulis middleware khusus seperti ini

export default function promiseMiddleware() {
  return (next) => (action) => {
    const { promise, ...rest } = action;
    if (!promise) {
      return next(action);
    }

    next({ ...rest, readyState: 'request' );
    return promise.then(
      (result) => next({ ...rest, result, readyState: 'success' }),
      (error) => next({ ...rest, error, readyState: 'failure' })
    );
  };
}

dan gunakan itu alih-alih yang default.
Ini akan memungkinkan Anda menulis pembuat tindakan asinkron seperti

function doSomethingAsync(userId) {
  return {
    type: SOMETHING,
    promise: requestSomething(userId),
    userId
  };
}

dan minta mereka berubah menjadi

{ type: SOMETHING, userId: 2, readyState: 'request' }
{ type: SOMETHING, userId: 2, readyState: 'success' }
{ type: SOMETHING, userId: 2, readyState: 'failure' }

Juga,

Saya pikir untuk bagian terakhir, maksud Anda:

{ type: SOMETHING, userId: 2, readyState: 'request' }
{ type: SOMETHING, userId: 2, result, readyState: 'success' }
{ type: SOMETHING, userId: 2, error, readyState: 'failure' }

Saya kira itu hanya tergantung pada apakah seseorang ingin membuat tindakan terpisah untuk panggilan balik success atau failure dari janji, daripada menggunakan yang dibuat secara otomatis.

Dalam contoh pemikiran Anda:

function makeASandwichWithSecretSauce(forPerson) {

  // Invert control!
  // Return a function that accepts `dispatch` so we can dispatch later.
  // Thunk middleware knows how to turn thunk async actions into actions.

  return function (dispatch) {
    return fetchSecretSauce().then(
      sauce => dispatch(makeASandwich(forPerson, sauce)),
      error => dispatch(apologize('The Sandwich Shop', forPerson, error))
    );
  };
}

maka tidak masuk akal untuk memiliki panggilan balik kesalahan umum untuk kegagalan mengambil saus rahasia, karena mungkin ada keadaan yang berbeda untuk mengambil saus.

Jadi saya bisa melihat model thunk sedikit lebih fleksibel.

Mungkin sesuatu seperti masuk atau mungkin bahkan beralih dari "indikator sibuk yang sedang berlangsung async" adalah contoh middleware yang lebih tepat?

@justin808

Keduanya baik-baik saja. Pilih apa yang kurang bertele-tele untuk Anda dan apa yang bekerja lebih baik untuk proyek Anda. Saran saya adalah mulai dengan menggunakan thunks, dan jika Anda melihat beberapa pola berulang, ekstrak ke middleware khusus. Anda juga bisa mencampurnya.

Saya telah membuat ActionStore untuk memisahkan status tindakan yang dipicu (memuat, berhasil, gagal) dari status lainnya. Tapi saya tidak tahu apakah ini bertentangan dengan dasar Redux/Flux. Saya telah memposting di stackoverflow tentang ini.

@gabrielgiussi Saya pikir https://github.com/acdlite/redux-promise juga dapat mencapai apa yang Anda inginkan tanpa Anda harus menyimpan janji di negara bagian. Negara seharusnya serializable setiap saat.

@wmertens terima kasih atas

@gabrielgiussi Saya tidak melihat terlalu dekat tetapi sepertinya Anda
menempatkan janji atau fungsi di toko. Bagaimanapun, proyek itu
juga harus bekerja dengan baik saya pikir.

Pada Senin, 10 Agustus 2015, 19:15 gabrielgiussi [email protected] menulis:

@wmertens https://github.com/wmertens terima kasih atas sarannya . saya akan
lihat repo tetapi mengapa status saya tidak dapat diserialisasi? Atau kamu
mengatakan itu hanya untuk saya perhatikan?


Balas email ini secara langsung atau lihat di GitHub
https://github.com/gaearon/redux/issues/99#issuecomment -129531103.

Tidak.
(diketik di ponsel, maaf singkat)

Sebenarnya, di toko saya meletakkan objek Action kustom, mereka hanya Immutable.Record dengan atribut sederhana (id, state, payload) dan pemicu tindakan yang membuat dan mengembalikan Promise, jadi saya tidak menempatkan Promises di Store. Tapi aku mungkin memecahkan sesuatu di tempat lain, je. Terima kasih @wmertens.

@gabrielgiussi

dan pemicu tindakan yang membuat dan mengembalikan Janji

Jangan memasukkan fungsi atau apa pun yang tidak dapat diserialisasi, ke dalam status.

Maaf. Saya mencoba untuk mengatakan

dan pemicu fungsi yang membuat dan mengembalikan Janji

Apa yang sebenarnya saya taruh di toko adalah objek Action (namanya bukan yang terbaik):

export default class Action extends Immutable.Record({state: 'idle', api: null, type: null, payload: null, id: null}){
load(){
  return this.set('state','loading');
}

succeed(){
  return this.set('state','succeeded');
}

fail(){
  return this.set('state','failed');
}

ended(){
  return this.get('state') != 'loading' && this.get('state') != 'idle';
}

endedWithSuccess(){
  return this.get('state') == 'succeeded';
}

endedWithFailure(){
  return this.get('state') == 'failed';
}

trigger() {
  return (dispatch) => {
    dispatch({type: this.get('type') + '_START', action: this});
    let payload = this.get('payload');
    this.get('api').call({dispatch,payload}).then((result) => {
      dispatch({type: this.get('type') + '_SUCCESS',id: this.get('id'), result: result.result});
    }).catch((result) => {
        dispatch({type: this.get('type') + '_FAIL',id: this.get('id'), result: result.result});
      });
  }
}
}

Saya telah membuat perpustakaan untuk memecahkan masalah ini (lihat #539) ini berfungsi dengan memiliki janji pengembalian middleware untuk tindakan yang tertunda, dan menunggu semua janji itu diselesaikan.

@gaearon kode ini Anda tulis https://github.com/rackt/redux/issues/99#issuecomment -112212639,

Apakah ini sesuatu yang termasuk perpustakaan redux atau sesuatu yang harus saya buat secara manual? Maaf jika ini adalah pertanyaan baru, baru saja masuk ke React / Flux (Redux). Baru saja memulai tutorial ini https://github.com/happypoulp/redux-tutorial

@banderson5144

Ini tidak termasuk. Itu hanya untuk memberi Anda gambaran tentang apa yang dapat Anda lakukan—tetapi Anda bebas melakukannya secara berbeda.
Sesuatu yang serupa diterbitkan sebagai https://github.com/pburtchaell/redux-promise-middleware.

Terima kasih atas info yang bermanfaat ini. Saya ingin memilih otak Anda untuk mengatur ulang toko -

  • Anda mengatur ulang status toko untuk pengguna baru
  • Anda menunggu beberapa tindakan asinkron selesai sebelum memberi pengguna toko dan html
  • Tindakan asinkron yang berjalan sebelumnya untuk pengguna lain selesai dan toko Anda tercemar

Bagaimana kalian memecahkan itu / punya ide bagaimana? Hanya toko baru untuk setiap pengguna yang akan berfungsi?

Apakah Anda berbicara tentang rendering server? Buat toko baru pada setiap permintaan. Kami memiliki panduan untuk rendering server di dokumen.

Terima kasih, saya akan melakukannya

Setelah mencoba memahami…

Apakah ini terlalu naif? (sepertinya tidak ada orang lain yang melakukan ini—saya pikir)

// server.js
app.use(function (req, res) {
    match({…}, function (error, redirectLocation, renderProps) {
        …

        if (renderProps) {
            const store = configureStore();

            const promises = renderProps.components.map(function (component, index) {
                if (typeof component.fetchData !== 'function') {
                    return false;
                }

                return component.fetchData(store.dispatch);
            });

            Promise.all(promises).then(function () {
                res.status(200).send(getMarkup(store, renderProps));
            });
        }
    })
});
// home.js
export class Home extends Component {
    static fetchData() {
        return Promise.all([
            dispatch(asyncAction);
        ]);
    },

    componentDidMount() {
        const { dispatch } = this.props;

        Home.fetchData(dispatch);
    }
}

export default connect()(Home);
// action.js
export function asyncAction() {
    return (dispatch, getState) => {
        dispatch(request);

        return fetch(…)
            .then(response => response.json())
            .then(data => dispatch(requestSuccess(data)))
        ;
    }
}

Saya juga mencoba untuk mencari tahu solusi untuk @mattybow 's pertanyaan https://github.com/rackt/redux/issues/99#issuecomment -112.980.776 (komponen bersarang mengelola data mengambil), tetapi tidak ada keberhasilan tersebut (tidak yakin tentang cara mengumpulkan janji dari componentWillMount ).

@chemoish Saya juga mencoba memahami rendering sisi server dengan reaksi dan redux. Contoh dalam dokumentasi tidak menangani kasus penggunaan ini dengan baik. Saya tidak ingin menentukan dan memasangkan setiap permintaan API di server dengan komponen saya lagi. Komponen hanya harus menentukan cara mendapatkan data yang diperlukan (yang kemudian harus diambil oleh server).

Solusi Anda terlihat cukup bagus untuk mencapai itu. Apakah ini berhasil untuk Anda? Terima kasih

Sunting: Saya benar bahwa "componentDidMount" tidak terpicu lagi di klien saat dirender di server?

@ ms88privat Saya belum mendapatkan banyak umpan balik tentang solusinya, juga belum menguji batasnya.

Namun, solusi yang diajukan di atas mengharuskan setiap halaman mengetahui data untuk semua komponen turunannya. Saya belum menggali lebih dalam apakah komponen bersarang khawatir tentang manajemen data itu sendiri (karena mengumpulkan janji bersarang).

Tampaknya melakukan apa yang Anda harapkan, jadi itu cukup baik untuk saya untuk saat ini.


componentDidMount akan dipicu lagi (Lihat https://facebook.github.io/react/docs/component-specs.html#mounting-componentdidmount). Anda dapat menggunakan metode siklus hidup ini atau lainnya yang sesuai dengan kebutuhan Anda.

Saya menyiasatinya dengan mencegah kode fetch dieksekusi jika toko sudah penuh (atau logika bisnis apa pun yang Anda inginkan).

Periksa https://github.com/reactjs/redux/blob/master/examples/async/actions/index.js#L47 untuk gagasan tentang apa yang saya bicarakan.

@kemoish

Saya menyiasatinya dengan mencegah kode pengambilan dijalankan jika toko sudah penuh

Oke, saya mengerti. Terima kasih.

Namun, solusi yang diajukan di atas mengharuskan setiap halaman mengetahui data untuk semua komponen turunannya.

Mungkin saya salah membaca solusi Anda, tetapi bukankah ini persyaratan yang diperlukan terlepas dari rendering sisi server? (misalnya itu harus membuat keadaan yang sama, jika saya menyegarkan pada rute saat ini, bahkan jika itu adalah SPA)

Itu akan, tetapi Anda mungkin ingin komponen bersarang mengelola pengambilan datanya sendiri, untuk alasan apa pun.

Misalnya, komponen yang diulang pada banyak halaman, tetapi setiap halaman tidak memiliki banyak kebutuhan pengambilan data.

@chemoish Saya tidak yakin apakah kita berada di halaman yang sama. Biarkan saya mencoba menjelaskan apa sudut pandang saya.

Misalnya saya mendapat tiga komponen bersarang:

  • komponen1 (pengambilan data statis1)

    • component2 (dataFetch2) statis

    • component3 (dataFetch3) statis

Masing-masing dari mereka memiliki metode "componentDidMount" sendiri, dengan deklarasi DataFetching sendiri (pengiriman tindakan melalui metode dataFetching statis).

Jika saya tidak memiliki rendering sisi server dan saya menyegarkan URL saat ini, komponen saya akan dipasang dan memicu semua tindakan yang diperlukan untuk memuat semua data yang diperlukan setelahnya.

Dengan rendering sisi server, fungsi match dan renderProps akan mengekstrak ketiga komponen, jadi saya dapat mengakses semua metode Pengambilan data statis, yang kemudian memungkinkan saya mengambil semua data yang diperlukan untuk rendering sisi server awal?

Apakah Anda memiliki referensi ke match function Anda dari contoh yang Anda berikan? Terima kasih.

@ms88privat renderProps.components adalah array komponen router, tidak lebih dalam dari itu. @chemoish berarti bahwa dengan implementasinya Anda tidak dapat menggambarkan kebutuhan pengambilan data pada komponen yang lebih dalam.

@DominicTobias thx, apakah Anda punya solusi untuk masalah ini? Apakah ada kemungkinan untuk mendapatkan semua komponen bersarang?

Mungkin ini bisa membantu? https://github.com/gaearon/react-side-effect
Digunakan untuk mengumpulkan semua tag meta dari elemen bersarang: https://github.com/nfl/react-helmet

Maaf karena menabrak diskusi ini lagi, tetapi saya baru-baru ini mengalami masalah yang sama tentang status pra-pengisian dengan tindakan async.

Saya dapat melihat bahwa @erikras memindahkan proyek boilerplate-nya ke redux-async-connect . Saya ingin tahu, apakah seseorang menemukan solusi lain?

@vtamborine Saya telah melihat https://github.com/markdalgleish/redial yang cukup membantu

Ya, saya telah melihat melalui itu. Tapi saya tidak mengerti, bagaimana memastikan data
mengambil ketagihan tidak akan berjalan kedua kalinya setelah kode inisialisasi ulang ke n
klien.
Pada , 18 арта 2016 . pada 22:54, Sean Matheson [email protected]
menulis:

@vtamborine https://github.com/vtamborine Saya telah melihat
https://github.com/markdalgleish/redial yang cukup membantu


Anda menerima ini karena Anda disebutkan.
Balas email ini secara langsung atau lihat di GitHub
https://github.com/reactjs/redux/issues/99#issuecomment -198517067

Juga ingin tahu apakah ada yang menemukan solusi stabil untuk tantangan ini. Saya suka boilerplate @erikras , tetapi seperti yang disebutkan @vtamborine , ia telah pindah ke redux-async-connect yang sepertinya bukan solusi jangka panjang yang stabil: #81 Apakah redux-async-connect mati? .

@vtamborine ada garpu yang tersedia di https://github.com/makeomatic/redux-connect dan terpelihara dengan baik. Ini memiliki API serupa dengan beberapa perubahan, periksa jika Anda tertarik

bagi mereka yang tertarik dengan solusi redux dengan middleware seperti yang disebutkan oleh @gaearon Saya punya contoh proyek yang mengimplementasikan teknik ini dan memungkinkan komponen itu sendiri meminta data yang mereka butuhkan di sisi server

https://github.com/peter-mouland/react-lego-2016#redux -with-promise-middleware

Bagaimana cara menguji pembuat tindakan dengan pendekatan ini?

Apakah halaman ini membantu?
0 / 5 - 0 peringkat