2016-07-14 128 views
6

La domanda:Redux: l'organizzazione di contenitori, componenti, azioni e riduttori

Qual è la migliore pratica più mantenibile e raccomandato per l'organizzazione di contenitori, componenti, azioni e riduttori in una grande React/Redux applicazione ?

La mia opinione:

tendenze attuali sembrano per organizzare collaterals Redux (azioni, riduttori, saghe ...) attorno al componente contenitore associato. per esempio.

/src 
    /components 
     /... 
    /contianers 
     /BookList 
      actions.js 
      constants.js 
      reducer.js 
      selectors.js 
      sagas.js 
      index.js 
     /BookSingle 
      actions.js 
      constants.js 
      reducer.js 
      selectors.js 
      sagas.js 
      index.js   
    app.js 
    routes.js 

Questo funziona benissimo! Anche se sembra esserci un paio di problemi con questo design.

I problemi:

Quando abbiamo bisogno di accedere actions, selectors o sagas da un altro contenitore sembra un anti-modello. Diciamo che abbiamo un contenitore globale /App con un riduttore/stato che memorizza le informazioni che usiamo sull'intera app, come le categorie e le enumerabili. Dopo il precedente esempio, con un albero di Stato:

{ 
    app: { 
     taxonomies: { 
      genres: [genre, genre, genre], 
      year: [year, year, year], 
      subject: [subject,subject,subject], 
     } 
    } 
    books: { 
     entities: { 
      books: [book, book, book, book], 
      chapters: [chapter, chapter, chapter], 
      authors: [author,author,author], 
     } 
    }, 
    book: { 
     entities: { 
      book: book, 
      chapters: [chapter, chapter, chapter], 
      author: author, 
     } 
    }, 
} 

Se vogliamo usare un selector dal contenitore /App all'interno della nostra /BookList contenitore abbiamo bisogno per ricreare in /BookList/selectors.js (sicuramente non va?) O importarlo da /App/selectors (sarà sempre lo stesso selettore ESATTO ..? no.). Entrambe queste valutazioni mi sembrano sub-ottimali.

Il primo esempio di questo caso d'uso è l'autenticazione (ah ... auth noi amiamo odiare voi) in quanto è un modello comune "effetto collaterale" MOLTO. Spesso è necessario accedere a /Auth sagas, azioni e selettori in tutta l'app. Potremmo avere i contenitori /PasswordRecover, /PasswordReset, /Login, /Signup .... In realtà nella nostra app il nostro contante /Auth non ha alcun componente reale!

/src 
    /contianers 
     /Auth 
      actions.js 
      constants.js 
      reducer.js 
      selectors.js 
      sagas.js 

Semplicemente contenente tutti i collaterali Redux per i vari e spesso non correlati contenitori di autorizzazione menzionati sopra.

+0

Sono curioso, con la tua struttura attuale come stai usando il tuo selettore? Supponiamo che un componente usi le funzioni dei selettori di 'BookList', puoi mostrarmi la funzione' mapStateToProps'? stai passando lo 'stato' attraverso? o il 'state.booklist' – xiaofan2406

risposta

5

Personalmente utilizzo la proposta ducks-modular-redux.

Non è il metodo "ufficiale" consigliato ma funziona benissimo per me. Ogni "anatra" contiene un file actionTypes.js, actionCreators.js, reducers.js, sagas.js e selectors.js. Non esiste alcuna dipendenza da altre anatre in questi file per evitare la dipendenza ciclica o duck circle, ogni "anatra" contiene solo la logica che deve gestire.

Poi, alla radice ho un components e un containers cartelle e alcuni file di root:

components/ cartella contiene tutti i componenti puri della mia app cartella

containers/ contiene contenitori creato da componenti puri sopra. Quando un contenitore ha bisogno di uno specifico selector che coinvolge molte "anatre", lo scrivo nello stesso file in cui ho scritto il componente <Container/> poiché è relativo a questo specifico contenitore. Se lo selector è condiviso accros contenitori multipli, lo creo in un file separato (o in un HoC che fornisce questi oggetti di scena).

rootReducers.js: espone semplicemente i riduttori di root mediante la combinazione di tutti i riduttori

rootSelectors.js espone il selettore principale per ogni fetta di Stato, ad esempio, nel tuo caso si potrebbe avere qualcosa di simile:

/* let's consider this state shape 

state = { 
    books: { 
     items: { // id ordered book items 
      ... 
     } 
    }, 
    taxonomies: { 
     items: { // id ordered taxonomy items 
      ... 
     } 
    } 
} 

*/ 
export const getBooksRoot = (state) => state.books 

export const getTaxonomiesRoot = (state) => state.taxonomies 

Permette di "nascondere" la forma dello stato all'interno di ogni anatra del file selectors.js. Poiché ogni selector riceve l'intero stato all'interno delle anatre, è sufficiente importare il corrispondente rootSelector all'interno dei file selector.js.

rootSagas.js compongono tutte le saghe dentro le anatre e gestire il flusso complesso che coinvolge molti "anatre".

Quindi nel tuo caso, la struttura potrebbe essere:

components/ 
containers/ 
ducks/ 
    Books/ 
     actionTypes.js 
     actionCreators.js 
     reducers.js 
     selectors.js 
     sagas.js 
    Taxonomies/ 
     actionTypes.js 
     actionCreators.js 
     reducers.js 
     selectors.js 
     sagas.js 
rootSelectors.js 
rootReducers.js 
rootSagas.js 

Quando i miei "anatre" sono abbastanza piccoli, spesso saltare la creazione delle cartelle e direttamente scrivo un ducks/Taxonomies.js file di ducks/Books.js o con tutti questi 5 file (actionTypes.js, actionCreators.js, reducers.js, selectors.js, sagas.js) uniti insieme.