2016-07-29 42 views
3

Ich habe Probleme herauszufinden, wie man Widget-Reduzierungen zusammenstellt und die Bäume im Tandem-Modus verarbeitet, so dass ich später den resultierenden Redux-Speicher für die Reduzierungen mithilfe von react-redux auf Requisiten im Renderbaum abbilden kann.Wie man gekapselten ui Zustand in einem redux Speicher auf Stützen mit react-redux abbildet?

Angenommen, ein Setup wie folgt. Ich versuche, eine NameWidget zu machen, dass ich überall in jedem App verwenden können:

NameView.jsx: 
... 
render() { 
    return <div> Name: {this.props.name} </div>; 
} 
... 

====

NameAction.js: 
... 
export const CHANGE_NAME = 'CHANGE_NAME'; 
... 
export function changeNameAction(newName) { 
    return { 
    type: CHANGE_NAME, 
    payload: newName, 
    }; 
} 

====

NameReducer.js: 

import { CHANGE_NAME } from './NameAction'; 

function ui(state = {name: ''}, action) { 
    switch(action.type) { 
    case(CHANGE_NAME): 
     return Object.assign({}, state, {name: action.payload}); 
     break; 
    default: 
     return state; 
    } 
} 

export default ui; 

====

NameWidget.js: 

import { connect } from 'react-redux'; 
import { NameView } from './NameView'; 

const mapStateToProps = (state) => { 
    return { 
    name: state.name, 
    }; 
}; 

const NameWidget = connect(
    mapStateToProps 
)(NameView); 

export default NameWidget; 

Wenn ich NameWidget direkt verwende, werde ich keine Probleme haben em:

NameApp.jsx: 

import { createStore } from 'redux'; 
import app from './NameReducers'; 
let store = createStore(app); 
... 
function render() { 
    return (
    <Provider store={store}> 
     <NameWidget/> 
    </Provider> 
); 
} 

Allerdings sehe ich nicht, wie man dieses modulare macht. Ich kann dieses Widget eigentlich nicht in etwas anderes einfügen. Angenommen, ich wollte, dies zu tun:

EncapsulatingView.jsx: 
... 
render() { 
    return (
    <div> 
     <NameWidget/> 
     <SomeOtherReduxWidget/> 
    </div> 
); 
} 
... 

====

EncapsulatingReducer.js: 
import { combineReducers } from 'redux' 
import nameWidget from '../name/NameReducer'; 
import someOtherWidget from '../someOther/SomeOtherReduxWidgetReducer'; 

export default combineReducers({nameWidget, someOtherWidget}); 

(Ich gehe davon aus, dass der verbleibende Code einschließlich connect() und creates() zum Einkapseln * liegt auf der Hand.)

fast funktioniert, solange alle Aktionen über alle eingekapselten Ansichten eindeutige Typen haben. Aber das Problem ist NameWidgets mapStateToProps; es muss von oben ändern, um einen neuen Weg zu seinem Abschnitt des Speichers zu verwenden:

... 
const mapStateToProps = (state) => { 
    return { 
    name: state.nameWidget.name, 
    }; 
}; 
... 

Und so weiter - je nachdem, wie Vorfahren combineReducers(), um ihre immer großartigere Super Widgets und Anwendungen zu definieren, die Nachkommen irgendwie müssen ändern, wie sie mapStateToProps() kompensieren. Dies bricht die Kapselung und Wiederverwendbarkeit von Code, und ich sehe nicht, wie ich es in react-redux umgehen kann. Wie kann ich Widgets definieren, indem ich combineReducers() zusammenfasse und den resultierenden Speicher dann den Requisiten dieser Widgets zuordne, und zwar auf einmal schreibende und überall verwendbare Weise?

(Apropos nichts, es scheint merkwürdig, dass wiederholt combinedReducers() eine Form erstellt, die von unten nach oben ist, aber dass mapStateToProps() stattdessen einen Top-Down-Ansatz "absoluter Pfad" zum Nachschlagen in diese Form annimmt.)

+0

Haben Sie auf den Export dieser Plan Code zu einem anderen Projekt oder wollten Sie das Widget in einer anderen Umgebung im selben Projekt erneut verwenden? – Gennon

+0

Kurzfristig ist projektspezifisch, aber wenn es ein gutes Muster/eine gute Lösung gibt, würde es hoffentlich beides unterstützen. – jdowdell

+0

Habt ihr mal [redux-form] (http://redux-form.com/5.3.1/#/getting-started) gesehen, wo sie einen definierten Namen (form) als eigenen Wurzelreduzierer benutzen? Auf diese Weise werden die Komponenten state.form für alle Werte betrachten. – Gennon

Antwort

1

Interessante Frage. Ich frage mich, ob es Ihrem Zweck dienen würde, eine Guid von der Eltern generiert und über Requisiten zu erhalten, und dann verwenden Sie einfach das als den Index Ihres Zustandsobjekts für diese Instanz Ihres Widgets. Wenn Sie also eine neue Instanz dieses Widgets erstellen, müssen Sie eine ID für dieses Element im übergeordneten Element erstellen und dieses dann in den Requisiten übergeben. Dann steht es der Methode connect zur Verwendung in mapStateToProps und mapDispatchToProps zur Verfügung . Ich nehme an, theoretisch könnten Sie diese übergeordnete Komponente selbst erstellen und sie könnte möglicherweise transparent sein, wenn sie verwendet wird, und einfach componentDidMount verwenden, um eine neue Guid zu generieren, die im Komponentenstatus gespeichert und an das untergeordnete Element übergeben wird.

+0

Danke John! Ich hatte über etwas Ähnliches nachgedacht, aber ich weiß nicht, wie ich das in den Requisiten "weitergeben" soll - können Sie es ausarbeiten? Es scheint mir, dass connect() im Wesentlichen aufgerufen wird, wenn ich den zurückgegebenen Typ importiere, nicht in render(), wenn ich diesen Typ verwende. Ich denke, ich könnte connect() in eine Funktion einbinden und sie dann während render() dynamisch aufrufen, indem ich diese Funktion an die angegebene ID weitergebe; aber ich bin nicht sicher über die Implikationen eines dynamisch generierten Typs jeden Aufruf zu rendern ... – jdowdell

+0

Also ich sage, dass Sie eine Elternkomponente hätten, und in ComponentDidMount (eigentlich ist ComponentWillMount wahrscheinlich der bessere Ort) würden Sie im Wesentlichen Rufen Sie 'this.setState ({childId: newGuid()})' auf, und rendern Sie dann in der render-Methode die untergeordnete Container-Komponente mit etwas wie ''. Mit Container-Komponente meine ich einfach die Komponente 'reduce only', die das Ergebnis des Aufrufs von 'connect' mit Ihrer tatsächlichen untergeordneten Komponente ist. – John

+0

Dann in Ihrer Verbindungsfunktion, wenn Sie die Parameter übergeben, die die Funktionen mapStateToProps und mapDispatchToProps sind, in _those_ Funktionen verwenden Sie ihren zweiten Parameter (die Requisiten der Komponente), um die ID zu verwenden. Zum Beispiel könnte Ihre Connect-Funktion wie folgt aussehen: https://jsbin.com/joyodaxazi/1/edit?js – John

0

Ich gab John die Kredit für diese Frage, weil er im Wesentlichen die richtige Magie für die Antwort zur Verfügung gestellt, vorbei "routing" Logik über MapStateToProps() eigeneProps Funktion arg übergeben. Aber ich habe meinen eigenen Dreh darauf bevorzugt. Im Wesentlichen, wenn Sie combineReducers() in einem EncapsulatingReducer.js, müssen Sie daran denken, ein uiStorePath Stütze in EncapsulatingView.js weitergeben müssen:

New NameWidget.js: 

const _ = require('lodash'); 
import { connect } from 'react-redux'; 
import { NameView } from './NameView'; 

const mapStateToProps = (state, props) => { 
    const uiStore = (
    (ownProps && _.has(ownProps, 'uiStorePath') && ownProps.uiStorePath && 
     ownProps.uiStorePath.length) ? 
     _.get(state, ownProps.uiStorePath) : // deep get...old versions of lodash would _.deepGet() 
     state 
); 

    return { 
    name: uiStore.name, 
    }; 
}; 

const NameWidget = connect(
    mapStateToProps 
)(NameView); 

export default NameWidget; 

====

EncapsulatingView.js: 

... 
render() { 
    const uiStorePathBase = ((this.props.uiStorePath &&   
     this.props.uiStorePath.length) ? 
    this.props.uiStorePath + '.' : 
    '' 
); 
    return (
    <div> 
     <NameWidget uiStorePath={uiStorePathBase+"nameWidget"}/> 
     <SomeOtherWidget uiStorePath={uiStorePathBase+"someOtherWidget"/> 
    </div> 
); 
} 
... 
Verwandte Themen