Redux: tindakan penembakan sebagai respons terhadap transisi rute di react-router

Dibuat pada 7 Jul 2015  ·  26Komentar  ·  Sumber: reduxjs/redux

Hai kawan,

Saya menggunakan react-router dan redux di aplikasi terbaru saya dan saya menghadapi beberapa masalah yang berkaitan dengan perubahan status yang diperlukan berdasarkan parameter dan kueri url saat ini.

Pada dasarnya saya memiliki komponen yang perlu memperbarui statusnya setiap kali url berubah. Status sedang diteruskan melalui props oleh redux dengan dekorator seperti itu

 @connect(state => ({
   campaigngroups: state.jobresults.campaigngroups,
   error: state.jobresults.error,
   loading: state.jobresults.loading
 }))

Saat ini saya menggunakan metode siklus hidup componentWillReceiveProps untuk menanggapi perubahan url yang berasal dari react-router karena react-router akan meneruskan props baru ke handler ketika url berubah menjadi this.props.params dan this.props.query - masalah utama dengan pendekatan ini adalah bahwa saya menjalankan tindakan dalam metode ini untuk memperbarui status - yang kemudian pergi dan melewati props baru komponen yang akan memicu metode siklus hidup yang sama lagi - jadi pada dasarnya membuat tanpa akhir loop, saat ini saya menetapkan variabel status untuk menghentikan hal ini terjadi.

  componentWillReceiveProps(nextProps) {
    if (this.state.shouldupdate) {
      let { slug } = nextProps.params;
      let { citizenships, discipline, workright, location } = nextProps.query;
      const params = { slug, discipline, workright, location };
      let filters = this._getFilters(params);
      // set the state accroding to the filters in the url
      this._setState(params);
      // trigger the action to refill the stores
      this.actions.loadCampaignGroups(filters);
    }
  }

Apakah ada pendekatan standar untuk memicu tindakan berdasarkan transisi rute ATAU dapatkah saya memiliki status toko yang terhubung langsung ke status komponen alih-alih meneruskannya melalui alat peraga? Saya telah mencoba menggunakan metode statis willTransitionTo tetapi saya tidak memiliki akses ke this.props.dispatch di sana.

docs ecosystem question

Komentar yang paling membantu

@deowk Ada dua bagian untuk masalah ini, menurut saya. Yang pertama adalah bahwa componentWillReceiveProps() bukanlah cara yang ideal untuk menanggapi perubahan status - sebagian besar karena ini memaksa Anda untuk berpikir secara imperatif , alih-alih secara reaktif seperti yang kita lakukan dengan Redux. Solusinya adalah menyimpan informasi router Anda saat ini (lokasi, parameter, kueri) _inside_ toko Anda. Kemudian semua negara Anda berada di tempat yang sama, dan Anda dapat berlangganan menggunakan API Redux yang sama dengan data Anda lainnya.

Triknya adalah membuat jenis tindakan yang menyala setiap kali lokasi router berubah. Ini mudah dalam versi 1.0 mendatang dari React Router:

// routeLocationDidUpdate() is an action creator
// Only call it from here, nowhere else
BrowserHistory.listen(location => dispatch(routeLocationDidUpdate(location)));

Sekarang status toko Anda akan selalu sinkron dengan status router. Itu memperbaiki kebutuhan untuk bereaksi secara manual terhadap perubahan parameter kueri dan setState() di komponen Anda di atas - cukup gunakan Konektor Redux.

<Connector select={state => ({ filter: getFilters(store.router.params) })} />

Bagian kedua dari masalah ini adalah Anda memerlukan cara untuk bereaksi terhadap perubahan status Redux di luar lapisan tampilan, katakanlah untuk mengaktifkan tindakan sebagai tanggapan atas perubahan rute. Anda dapat terus menggunakan componentWillReceiveProps untuk kasus sederhana seperti yang Anda jelaskan, jika Anda mau.

Untuk sesuatu yang lebih rumit, saya merekomendasikan menggunakan RxJS jika Anda terbuka untuk itu. Inilah yang dirancang untuk observasi - aliran data reaktif.

Untuk melakukan ini di Redux, pertama-tama buat urutan status penyimpanan yang dapat diamati. Anda dapat melakukan ini menggunakan observableFromStore() redux-rx.

import { observableFromStore } from 'redux-rx';
const state$ = observableFromStore(store);

Maka itu hanya masalah menggunakan operator yang dapat diamati untuk berlangganan perubahan status tertentu. Berikut contoh pengalihan ulang dari halaman login setelah login berhasil:

const didLogin$ = state$
  .distinctUntilChanged(state => !state.loggedIn && state.router.path === '/login')
  .filter(state => state.loggedIn && state.router.path === '/login');

didLogin$.subscribe({
   router.transitionTo('/success');
});

Implementasi ini jauh lebih sederhana daripada fungsionalitas yang sama menggunakan pola imperatif seperti componentDidReceiveProps() .

Semoga membantu!

Semua 26 komentar

@deowk Anda dapat membandingkan this.props dengan nextProps untuk melihat apakah data yang relevan berubah. Jika tidak berubah, Anda tidak perlu memicu tindakan lagi dan dapat keluar dari loop tak terbatas.

@Johanneslumpe : Dalam kasus saya campaigngroups adalah kumpulan objek yang agak besar, tampaknya jenis tidak efisien menggunakan perbandingan objek yang dalam sebagai kondisi dengan cara ini?

@deowk jika Anda menggunakan data yang tidak dapat diubah, Anda cukup membandingkan referensi

@deowk Ada dua bagian untuk masalah ini, menurut saya. Yang pertama adalah bahwa componentWillReceiveProps() bukanlah cara yang ideal untuk menanggapi perubahan status - sebagian besar karena ini memaksa Anda untuk berpikir secara imperatif , alih-alih secara reaktif seperti yang kita lakukan dengan Redux. Solusinya adalah menyimpan informasi router Anda saat ini (lokasi, parameter, kueri) _inside_ toko Anda. Kemudian semua negara Anda berada di tempat yang sama, dan Anda dapat berlangganan menggunakan API Redux yang sama dengan data Anda lainnya.

Triknya adalah membuat jenis tindakan yang menyala setiap kali lokasi router berubah. Ini mudah dalam versi 1.0 mendatang dari React Router:

// routeLocationDidUpdate() is an action creator
// Only call it from here, nowhere else
BrowserHistory.listen(location => dispatch(routeLocationDidUpdate(location)));

Sekarang status toko Anda akan selalu sinkron dengan status router. Itu memperbaiki kebutuhan untuk bereaksi secara manual terhadap perubahan parameter kueri dan setState() di komponen Anda di atas - cukup gunakan Konektor Redux.

<Connector select={state => ({ filter: getFilters(store.router.params) })} />

Bagian kedua dari masalah ini adalah Anda memerlukan cara untuk bereaksi terhadap perubahan status Redux di luar lapisan tampilan, katakanlah untuk mengaktifkan tindakan sebagai tanggapan atas perubahan rute. Anda dapat terus menggunakan componentWillReceiveProps untuk kasus sederhana seperti yang Anda jelaskan, jika Anda mau.

Untuk sesuatu yang lebih rumit, saya merekomendasikan menggunakan RxJS jika Anda terbuka untuk itu. Inilah yang dirancang untuk observasi - aliran data reaktif.

Untuk melakukan ini di Redux, pertama-tama buat urutan status penyimpanan yang dapat diamati. Anda dapat melakukan ini menggunakan observableFromStore() redux-rx.

import { observableFromStore } from 'redux-rx';
const state$ = observableFromStore(store);

Maka itu hanya masalah menggunakan operator yang dapat diamati untuk berlangganan perubahan status tertentu. Berikut contoh pengalihan ulang dari halaman login setelah login berhasil:

const didLogin$ = state$
  .distinctUntilChanged(state => !state.loggedIn && state.router.path === '/login')
  .filter(state => state.loggedIn && state.router.path === '/login');

didLogin$.subscribe({
   router.transitionTo('/success');
});

Implementasi ini jauh lebih sederhana daripada fungsionalitas yang sama menggunakan pola imperatif seperti componentDidReceiveProps() .

Semoga membantu!

Kami membutuhkan ini di dokumen baru. Ditulis dengan sangat baik.

@acdlite : Terima kasih banyak, saran Anda sangat membantu saya, saya kesulitan dengan hal berikut -> mengubah status di toko dengan memicu pembuat tindakan sebagai tanggapan atas perubahan url.

Saya ingin memicu pembuat tindakan dalam metode statis willTransitionTo karena ini sepertinya satu-satunya pilihan di react-router v0.13.3

class Results extends Component  {
..........
}

Results.willTransitionTo = function (transition, params, query) {
 // how do I get a reference to dispatch here?
};

@deowk Dugaan saya adalah Anda memanggil dispatch langsung pada instance redux:

const redux = createRedux(stores);
BrowserHistory.listen(location => redux.dispatch(routeLocationDidUpdate(location)));

@deowk Saat ini saya mengirimkan perubahan url saya di react router 0.13.3 seperti ini:

Router.run(routes, Router.HistoryLocation, function(Handler, locationState) {
   dispatch(routeLocationDidUpdate(locationState))
   React.render(<Handler/>, appEl);
});

@acdlite dijelaskan dengan sangat baik! : +1:
Setuju itu harus ada di dokumen baru juga :)

Sekadar catatan, BrowserHistory.history() telah berubah menjadi BrowserHistory.addChangeListener() .

https://github.com/rackt/react-router/blob/master/modules/BrowserHistory.js#L57

EDIT:

Yang mungkin terlihat seperti ini:

history.addChangeListener(() => store.dispatch(routeLocationDidUpdate(location)));

Dan bagaimana dengan status awal peredam itu, @pburtchaell?

Saya memiliki pengaturan berikut:

// client.js
class App extends Component {
  constructor(props) {
    super(props);
    this.history = new HashHistory();
  }

  render() {
    return (
      <Provider store={store}>
         {renderRoutes.bind(null, this.history)}
      </Provider>
    );
  }
}

// routes.js
export default function renderRoutes(history) {

  // When the route changes, dispatch that information to the store.
  history.addChangeListener(() => store.dispatch(routeLocationDidUpdate(location)));

  return (
    <Router history={history}>
      <Route component={myAppView}>
        <Route path="/" component={myHomeView}  />
      </Route>
    </Router>
  );
};

Ini mengaktifkan pembuat tindakan routeLocationDidUpdate() setiap kali rute berubah dan saat aplikasi pertama kali dimuat.

@pburtchaell dapatkah Anda membagikan routeLocationDidUpdate ?

@gyok Tentu. Itu adalah pencipta aksi.

// actions/location.js
export function routeLocationDidUpdate(location) {
  return {
    type: 'LOCATION_UPDATE',
    payload: {
      ...location,
    },
  };
}

@pburtchaell jadi dalam contoh Anda saya tidak mengerti di mana Anda mengambil data yang diperlukan untuk rute tertentu

Berikut adalah versi lengkap dari contoh saya di atas:

https://github.com/acdlite/redux-react-router/blob/master/README.md#bonus -reacting-to-state-changes-with-redux-rx

Saya berasumsi @pburtchaell akan menggunakan versi async dari kode itu untuk mendapatkan data (dari panggilan REST dll). Apa yang saya tidak yakin tentang itu? Saya berasumsi itu kemudian pergi ke toko, tetapi apakah toko itu akan menjadi locationStore yang kemudian

<strong i="7">@connect</strong>

untuk masuk, katakanlah masuk

<Comments />

yang mana sudah 'terhubung' (berlangganan) ke commentStore? Atau tambahkan saja daftar tindakan lokasi ini di commentsStore?

Seperti url

/comments/123

akan selalu 'digabungkan' ke komponen komentar, jadi bagaimana cara menggunakannya kembali? Maaf kalau ini bodoh, sangat bingung disini. Yang mana ada contoh kuat yang bisa saya temukan.

Saya juga bergulat dengan ini, mencari tahu bagaimana memanggil metode statis fetchData(dispatch, params) (statis, karena server memanggilnya untuk mengirimkan tindakan FETCH asinkron sebelum render awal).

Karena referensi ke dispatch ada dalam konteks, saya berpikir cara terbersih untuk mendapatkan ini untuk pemanggilan sisi-klien fetchData adalah komponen mirip Connector yang memanggil fetchData atau shouldFetchData , atau minta callback select mendapatkan referensi ke dispatch bersama dengan state , sehingga kita dapat memeriksa arus status store dan kirim tindakan FETCH sesuai kebutuhan.

Yang terakhir ini lebih ringkas tetapi mematahkan fakta bahwa select harus tetap menjadi fungsi murni. Saya akan melihat yang pertama.

Solusi saya adalah ini, meskipun cukup disesuaikan dengan pengaturan saya (metode statis fetchData(dispatch, state) yang mengirimkan async _FETCH tindakan) itu akan memanggil callback apa pun yang diteruskan kepadanya dengan properti yang sesuai: https: // gist.github.com/grrowl/6cca2162e468891d8128 - meskipun demikian, tidak menggunakan willTransitionTo, jadi Anda tidak akan dapat menunda transisi halaman.

Kami pasti perlu menambahkan dokumen! Mungkin di bagian "Menggunakan dengan react-router".

Kami akan memiliki cara resmi untuk melakukannya setelah rilis 1.0.

Senang mendengar @gaearon.

Maka itu hanya masalah menggunakan operator yang dapat diamati untuk berlangganan perubahan status tertentu.

Saya memiliki aliran yang dapat diamati yang disiapkan untuk menjaga toko saya tetap sinkron dengan perubahan rute apa pun, dan saya juga memiliki pengaturan aliran untuk menonton toko saya. Saya tertarik untuk bertransisi ke rute baru ketika nilai berubah di toko saya, khususnya ketika nilai berubah dari undefined menjadi string yang valid sebagai akibat dari berhasil mengirimkan formulir di tempat lain di aplikasi saya.

Kedua aliran disiapkan dan berfungsi, dan toko sedang diperbarui, tetapi saya baru mengenal Rx, dan mengalami masalah dengan kueri yang dapat diamati ... ada petunjuk? Apakah saya di jalur yang benar? Cukup yakin itu ada hubungannya dengan distinctUntilChanged tetapi memanggang mie saya saat ini, bantuan apa pun dihargai :)

EDIT: Ack! Maaf, jawab pertanyaan saya sendiri. Kode semu di bawah ini. Beri tahu saya jika terlihat mengerikan?

const didChangeProject$ = state$
  .distinctUntilChanged(state => state.project._id)
  .filter(state => typeof state.project._id !== 'undefined');

Menutup untuk # 177. Saat kita mendokumentasikan perutean, kita harus mencakup semua skenario: pengalihan pada perubahan status, pengalihan saat panggilan balik permintaan, dll.

Saya tahu ini ditutup .. tetapi dengan React 0.14 dan berbagai dependensi 1.0 dalam beta sekarang, apakah ada pembaruan untuk ini dan jika tidak, dapatkah tutorial seputar kemampuan baru di React 0.14, react-router, redux, dll. tertulis? Tampaknya ada banyak dari kita yang mencoba untuk tetap up to date dengan perubahan yang akan datang sambil mempelajari / memahami semua ini pada saat yang bersamaan. Saya tahu banyak hal dalam keadaan berubah (pelesetan .. uh .. tidak dimaksudkan), tetapi sepertinya saya melihat potongan-potongan dari contoh yang berbeda yang tidak bermain bagus satu sama lain dan setelah beberapa hari saya masih tidak bisa menjalankan aplikasi yang diperbarui dengan perutean. Kemungkinan besar kesalahan saya sendiri karena saya masih sedikit berjuang dengan memahami bagaimana semua ini bekerja, hanya berharap tutorial yang diperbarui dengan hal-hal terbaru segera keluar.

Sampai ada tutorial, silakan melihat examples/real-world yang memiliki perutean. Ini bukan "terbaru dan terhebat" sekarang tetapi berfungsi dengan baik.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat

Masalah terkait

dmitry-zaets picture dmitry-zaets  ·  3Komentar

mickeyreiss-visor picture mickeyreiss-visor  ·  3Komentar

rui-ktei picture rui-ktei  ·  3Komentar

ms88privat picture ms88privat  ·  3Komentar

timdorr picture timdorr  ·  3Komentar