Redux uygulamasında kod bölme için redüktörleri dinamik olarak nasıl yükleyebilirim?


189

Redux'a göç edeceğim.

Uygulamam birçok parçadan (sayfalar, bileşenler) oluşuyor, bu yüzden birçok redüktör oluşturmak istiyorum. Redux örnekleri combineReducers(), bir redüktör oluşturmak için kullanmam gerektiğini gösteriyor .

Ayrıca anladığım kadarıyla Redux uygulamasının bir mağazası olmalı ve uygulama başladıktan sonra oluşturulur. Mağaza oluşturulurken kombine redüktörümü geçmeliyim. Uygulama çok büyük değilse bu mantıklıdır.

Ancak, birden fazla JavaScript paketi oluşturursam ne olur? Örneğin, uygulamanın her sayfasının kendi paketi vardır. Bu durumda bir kombine redüktörün iyi olmadığını düşünüyorum. Redux'un kaynaklarını inceledim ve buldumreplaceReducer() işlev . İstediğim gibi görünüyor.

Uygulamam ve kullandığım her parça için kombine redüktör oluşturabilirim replaceReducer() parçaları arasında hareket ederken .

Bu iyi bir yaklaşım mı?

Yanıtlar:


245

Güncelleme: Twitter'ın nasıl yaptığını da görün .

Bu tam bir cevap değil, ama başlamanıza yardımcı olmalı. Ben ediyorum Not eski düşürücüler kenara atmak sadece kombinasyon listesine yenilerini ekleyerek -Bunu. Eski redüktörleri atmak için hiçbir neden göremiyorum - en büyük uygulamada bile binlerce dinamik modüle sahip olmanız pek mümkün değil , bazı redüktörlerin bağlantısını kesmek .

reducers.js

import { combineReducers } from 'redux';
import users from './reducers/users';
import posts from './reducers/posts';

export default function createReducer(asyncReducers) {
  return combineReducers({
    users,
    posts,
    ...asyncReducers
  });
}

store.js

import { createStore } from 'redux';
import createReducer from './reducers';

export default function configureStore(initialState) {
  const store = createStore(createReducer(), initialState);
  store.asyncReducers = {};
  return store;
}

export function injectAsyncReducer(store, name, asyncReducer) {
  store.asyncReducers[name] = asyncReducer;
  store.replaceReducer(createReducer(store.asyncReducers));
}

routes.js

import { injectAsyncReducer } from './store';

// Assuming React Router here but the principle is the same
// regardless of the library: make sure store is available
// when you want to require.ensure() your reducer so you can call
// injectAsyncReducer(store, name, reducer).

function createRoutes(store) {
  // ...

  const CommentsRoute = {
    // ...

    getComponents(location, callback) {
      require.ensure([
        './pages/Comments',
        './reducers/comments'
      ], function (require) {
        const Comments = require('./pages/Comments').default;
        const commentsReducer = require('./reducers/comments').default;

        injectAsyncReducer(store, 'comments', commentsReducer);
        callback(null, Comments);
      })
    }
  };

  // ...
}

Bunu ifade etmenin daha güzel bir yolu olabilir - sadece fikri gösteriyorum.


13
Bu tür işlevselliklerin projeye eklendiğini görmek isterim. Kod bölme ve büyük uygulamalarla uğraşırken redüktörleri dinamik olarak ekleme zorunluluğu vardır. Bazı kullanıcılar tarafından erişilemeyen tüm alt ağaçlara sahibim ve tüm redüktörleri yüklemek bir atıktır. Redux-ignore ile bile büyük uygulamalar gerçekten redüktörleri toplayabilir.
JeffBaumgardt

2
Bazen, önemsiz bir şeyi 'optimize etmek' daha büyük bir israftır.
XML

1
Umarım yukarıdaki yorum mantıklı ... ben oda bitti gibi. Dinamik olarak farklı rotalardan yüklendiğinde Ama temelde bizim devlet ağaç üzerinde tek dal halinde bir araya düşürücüler var için kolay bir yol görmüyorum /homepagedaha o şubenin yüklenen ve daha sonra kullanıcı kendi gittiğinde profile.nasıl kullanılacağına ilişkin bir örnek bunu yapmak harika olurdu. Aksi takdirde devlet user-permissionsuser-personal
ağacımı

1
İlk durumum varsa nasıl davranmalıyım?
Stalso

3
github.com/mxstbr/react-boilerplate kazan plakası , redüktörleri yüklemek için burada belirtilen tekniği kullanır.
Pouya Sanooei

25

Geçerli bir uygulamada bu şekilde uyguladım (GitHub sorunundan Dan koduna dayanarak!)

// Based on https://github.com/rackt/redux/issues/37#issue-85098222
class ReducerRegistry {
  constructor(initialReducers = {}) {
    this._reducers = {...initialReducers}
    this._emitChange = null
  }
  register(newReducers) {
    this._reducers = {...this._reducers, ...newReducers}
    if (this._emitChange != null) {
      this._emitChange(this.getReducers())
    }
  }
  getReducers() {
    return {...this._reducers}
  }
  setChangeListener(listener) {
    if (this._emitChange != null) {
      throw new Error('Can only set the listener for a ReducerRegistry once.')
    }
    this._emitChange = listener
  }
}

Uygulamanızı önyüklerken giriş paketine eklenecek indirgeyicileri geçirerek bir kayıt defteri örneği oluşturun:

// coreReducers is a {name: function} Object
var coreReducers = require('./reducers/core')
var reducerRegistry = new ReducerRegistry(coreReducers)

Ardından depoyu ve yolları yapılandırırken, redüktör kayıt defterini verebileceğiniz bir işlev kullanın:

var routes = createRoutes(reducerRegistry)
var store = createStore(reducerRegistry)

Bu işlevler aşağıdaki gibi görünür:

function createRoutes(reducerRegistry) {
  return <Route path="/" component={App}>
    <Route path="core" component={Core}/>
    <Route path="async" getComponent={(location, cb) => {
      require.ensure([], require => {
        reducerRegistry.register({async: require('./reducers/async')})
        cb(null, require('./screens/Async'))
      })
    }}/>
  </Route>
}

function createStore(reducerRegistry) {
  var rootReducer = createReducer(reducerRegistry.getReducers())
  var store = createStore(rootReducer)

  reducerRegistry.setChangeListener((reducers) => {
    store.replaceReducer(createReducer(reducers))
  })

  return store
}

İşte bu kurulumla oluşturulan temel bir canlı örnek ve kaynağı:

Ayrıca tüm redüktörleriniz için sıcak yeniden yüklemeyi mümkün kılmak için gerekli yapılandırmayı da kapsar.


Teşekkürler @jonny, sadece bir kafa, örnek şimdi bir hata atıyor.
Jason

createReducer () bildirimi cevabınızda eksik (Dan Abrahamov'un cevabında olduğunu biliyorum, ancak karışıklığı önleyeceğini düşünüyorum)
Packet Tracer

6

Artık redux deposuna enjekte edici redüktörler ekleyen bir modül var. Buna Redux Enjektör denir .

İşte nasıl kullanılır:

  1. Redüktörleri birleştirmeyin. Bunun yerine, bunları normalde olduğu gibi birleştirmeden işlevlerin (iç içe) bir nesnesine koyun.

  2. Redux'dan createStore yerine redux-enjektörden createInjectStore kullanın.

  3. Yeni redüktörleri injectReducer ile enjekte edin.

İşte bir örnek:

import { createInjectStore, injectReducer } from 'redux-injector';

const reducersObject = {
   router: routerReducerFunction,
   data: {
     user: userReducerFunction,
     auth: {
       loggedIn: loggedInReducerFunction,
       loggedOut: loggedOutReducerFunction
     },
     info: infoReducerFunction
   }
 };

const initialState = {};

let store = createInjectStore(
  reducersObject,
  initialState
);

// Now you can inject reducers anywhere in the tree.
injectReducer('data.form', formReducerFunction);

Tam Açıklama: Modülün yaratıcısıyım.


4

Ekim 2017 itibarıyla:

  • Reedux

    Dan'ın önerisini ve daha fazlasını mağazanıza, projenize veya alışkanlıklarınıza dokunmadan uygular

Başka kütüphaneler de var, ancak çok fazla bağımlılıkları olabilir, daha az örnek, karmaşık kullanım, bazı orta yazılımlarla uyumsuz olabilir veya eyalet yönetiminizi yeniden yazmanızı gerektirebilir. Reedux'un giriş sayfasından kopyalandı:


2

Bir Redux uygulamasının modüle edilmesine yardımcı olan ve Redüktörleri ve ara yazılımları dinamik olarak eklemeye / kaldırmaya izin veren yeni bir kütüphane yayınladık.

Lütfen https://github.com/Microsoft/redux-dynamic-modules adresine bakın.

Modüller aşağıdaki avantajları sağlar:

  • Modüller uygulama genelinde veya birden fazla benzer uygulama arasında kolayca yeniden kullanılabilir.

  • Bileşenler, ihtiyaç duydukları modülleri bildirir ve redux-dinamik modüller, modülün bileşen için yüklenmesini sağlar.

  • Modüller dinamik olarak mağazadan eklenebilir / çıkarılabilir, ör. bir bileşen takıldığında veya kullanıcı bir eylem gerçekleştirdiğinde

Özellikleri

  • Redüktörleri, ara katman yazılımlarını ve durumu tek, yeniden kullanılabilir bir modülde gruplandırın.
  • İstediğiniz zaman bir Redux mağazasından modül ekleyin ve çıkarın.
  • Bir bileşen oluşturulduğunda otomatik olarak modül eklemek için birlikte verilen bileşeni kullanın
  • Uzantılar, redux-saga ve redux-gözlemlenebilir dahil popüler kütüphanelerle entegrasyon sağlar

Örnek Senaryolar

  • Tüm redüktörlerinizin kodunu önden yüklemek istemezsiniz. Bazı redüktörler için bir modül tanımlayın ve modülünüzü çalışma zamanında indirmek ve eklemek için DynamicModuleLoader ve tepki verilebilir gibi bir kütüphane kullanın.
  • Uygulamanızın farklı alanlarında yeniden kullanılması gereken bazı ortak redüktörleriniz / ara yazılımlarınız var. Bir modül tanımlayın ve kolayca bu alanlara ekleyin.
  • Benzer durumu paylaşan birden çok uygulama içeren bir mono repo var. Bazı modülleri içeren bir paket oluşturun ve bunları uygulamalarınızda yeniden kullanın

0

İşte kod bölme ve redux mağazaları ile başka bir örnek , bence oldukça basit ve zarif. Çalışan bir çözüm arayanlar için oldukça yararlı olabileceğini düşünüyorum.

Bu mağaza biraz basitleştirilmiş, devlet nesnenizde bir ad alanına (redüktör.adı) sahip olmaya zorlamıyor, elbette isimlerle bir çarpışma olabilir, ancak redüktörleriniz için bir adlandırma kuralı oluşturarak bunu kontrol edebilirsiniz ve iyi olmalı.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.