2016-08-08 51 views
4

Manchmal wäre es nützlich, einen Speicher pro Instanz in Redux-Anwendungen zu erstellen. Der Schöpfer von Redux selbst erstellt einen Gist, das beschreibt, wie dieses Ziel erreichen: https://gist.github.com/gaearon/eeee2f619620ab7b55673a4ee2bf8400Wie erstellt man einen Store pro Instanz in Redux?

ich diese Frage gestellt schon im Gist aber ich denke, Stackoverflow zu einem besseren Ort für diese Frage sei:

Ich frage mich, wie man dispatch Aktionen in den komponenteneigenen Spezialspeicher? Gibt es eine Möglichkeit, auf die store -prop der <Provider /> für jede <SubApp /> (und ihre Kind-Komponenten) zuzugreifen?

Zum Beispiel: Ich habe einige API-Klassen, die dispatch nach dem Abrufen von Daten von einem Remote-Server aufrufen. Aber da ich den "normalen" Speicher nicht importieren kann, was wäre der beste Weg, mit benutzerdefinierten Speichern umzugehen, um sie für andere Klassen/Dateien/Dienste verfügbar zu machen?

Update 1

Also habe ich es funktioniert, aber ich denke, es ist eine wirklich schmutzige Art und Weise (beachten Sie die UGLY? Kommentare in den Code):

Anbieter:

einen Speicher erstellen pro durch Erstellen des Speichers im Konstruktor:

export default class OffersGridProvider extends React.Component { 
    constructor(props) { 
    super(props) 
    this.store = createStore(reducers); 
    } 

    render() { 
    return (
     <Provider store={this.store}> 
     <OffersGridContainer offers={this.props.offers} /> 
     </Provider> 
    ); 
    } 
} 

Container:

Der Anbieter injiziert eine dispatch Methode für dieses Shop meine OffersGridContainer die ich Aktionen können nur in den Laden dieser Instanz versenden:

class OffersGridContainer extends React.Component {  
    componentDidMount() { 

    // UGLY??? 
    const { dispatch } = this.props; 

    let destinationIds = []; 
    this.props.offers.forEach((offer) => { 
     offer.to.forEach((destination) => { 
     destinationIds.push(destination.destination); 
     }); 
    }); 

    // MORE UGLY??? 
    destinationsApi.getDestinations(dispatch, destinationIds); 
    } 

    render() { 
    return (
     <OffersGridLayout destinations={this.props.destinations} /> 
    ); 
    } 
} 

const mapStateToProps = function(store) { 
    return { 
    destinations: store.offersGridState.destinations 
    }; 
} 

export default connect(mapStateToProps)(OffersGridContainer); 

API-Methode:

Verwenden Sie einfach die dispatch -Methode, die ich als Argument für meine API-Methode übergeben habe:

export function getDestinations(dispatch, ids) { 
    const url = $('meta[name="site-url"]').attr('content'); 

    const filter = ids.map((id) => { 
    return `filter[post__in][]=${id}`; 
    }).join('&'); 

    return axios.get(`${url}/wp-json/wp/v2/destinations?filter[posts_per_page]=-1&${filter}`) 
    .then(response => { 
     dispatch(getOffersGridSuccess(response.data)); 
     return response; 
    }); 
} 

Update 2

erhielt nur einen Hinweis auf mapDispatchToProps in den Kommentaren, so dass meine Container nun wie folgt aussieht:

class OffersGridContainer extends React.Component {  
    componentDidMount() { 
    let destinationIds = []; 

    this.props.offers.forEach((offer) => { 
     offer.to.forEach((destination) => { 
     destinationIds.push(destination.destination); 
     }); 
    }); 

    this.props.getDestinations(destinationIds); 
    } 

    render() { 
    return (
     <OffersGridLayout destinations={this.props.destinations} /> 
    ); 
    } 
} 

const mapStateToProps = function(store) { 
    return { 
    destinations: store.offersGridState.destinations 
    }; 
} 

const mapDispatchToProps = function(dispatch) { 
    return { 
    getDestinations: function(ids) { 
     return destinationsApi.getDestinations(dispatch, ids); 
    } 
    } 
} 

export default connect(mapStateToProps, mapDispatchToProps)(OffersGridContainer); 

Update 3 (die endgültige Antwort)

Jetzt ist alles funktioniert!Hier ist zu Code:

Anbieter:

export default class OffersGridProvider extends React.Component {  
    constructor(props) { 
    super(props) 
    this.store = createStore(reducers, applyMiddleware(thunk)); 
    } 

    render() { 
    return (
     <Provider store={this.store}> 
     <OffersGridContainer offers={this.props.offers} /> 
     </Provider> 
    ); 
    } 
} 

Container:

class OffersGridContainer extends React.Component { 
    componentDidMount() { 
    const destinationIds = this.props.offers.reduce((acc, offer) => { 
     return [...acc, ...offer.to.map(d => d.destination)]; 
    }, []); 

    this.props.getDestinations(destinationIds); 
    } 

    render() { 
    return (
     <OffersGridLayout destinations={this.props.destinations} /> 
    ); 
    } 
} 

const mapStateToProps = function(store) { 
    return { 
    destinations: store.offersGridState.destinations 
    }; 
} 

const mapDispatchToProps = function(dispatch) { 
    return { 
    getDestinations: function(ids) { 
     return dispatch(destinationsApi.getDestinations(ids)); 
    } 
    } 
} 

export default connect(mapStateToProps, mapDispatchToProps)(OffersGridContainer); 

API-Methode:

export function getDestinations(ids) { 
    return function(dispatch) { 
    const url = $('meta[name="site-url"]').attr('content'); 

    const filter = ids.map((id) => { 
     return `filter[post__in][]=${id}`; 
    }).join('&'); 

    return axios.get(`${url}/wp-json/wp/v2/destinations?filter[posts_per_page]=-1&${filter}`) 
     .then(response => { 
     return dispatch(getOffersGridSuccess(response.data)); 
     }); 
    } 
} 
+0

Es gibt einen zweiten Parameter 'connect':' mapDispatchToProps', die Ihnen erlaubt, zu halten die Komponente, die den Versand nicht kennt. –

+0

Danke! Könnten Sie bitte mein Update überprüfen? – Slevin

+0

Nächste Aktualisierung: Ich sollte 'thunk' als Middleware verwenden, damit ich' dispose' nicht als Argument übergeben muss und eine Funktion in meiner 'getDestinations' Methode zurückgeben muss. – Slevin

Antwort

1

statt api Aufrufe in Komponenten direkt machen, schlage ich vor, Sie verwenden redux-thunk Paket.

Als nächstes sollten Sie mapDispatchToProps Funktion als zweites Argument an connect Funktion übergeben, Aktion Schöpfer Funktionen Komponente zu injizieren:

import { getDestinations } from '../actions'; 

class OffersGridContainer extends React.Component {  
    componentDidMount() { 
    // Tip. 
    const destinationIds = this.props.offers.reduce((acc, offer) => { 
     return [...acc, ...offer.to.map(d => d.destination)]; 
    }, []); 

    // instead `destinationsApi.getDestinations(dispatch, destinationIds)` 
    // call action creator function 
    this.props.getDestinations(destinationIds); 
    } 

    render() { 
    return (
     <OffersGridLayout destinations={this.props.destinations} /> 
    ); 
    } 
} 

const mapStateToProps = function(store) { 
    return { 
    destinations: store.offersGridState.destinations 
    }; 
} 

const mapDispatchToProps = function(dispatch) { 
    return { 
    // this function will be available in component as 
    // `this.props.getDestinations`. 
    getDestinations: function(destinationIds) { 
     dispatch(getDestinations(destinationIds)); 
    } 
    }; 
} 

export default connect(mapStateToProps, mapDispatchToProps)(OffersGridContainer); 
+0

Danke! Ich habe den ersten Ansatz versucht, aber der Laden bleibt gleich. Ich möchte meine "SubApp" einige Male rendern, aber wenn sich die Daten in einer "SubApp" ändern, werden auch alle anderen Instanzen von "SubApp" aktualisiert. Ich denke, der 'customStore' muss im' Konstruktor' erstellt werden, aber dann verliere ich die Möglichkeit, ihn zu exportieren. Ich werde eine kleine Fiedel erstellen und den Link in einen neuen Kommentar einfügen. – Slevin

+0

@Slevin, ok, es wäre einfacher mit der Geige zu verstehen. – 1ven

+0

Oh, ich habe es herausgefunden (aber ich denke nicht, dass dies "Best Practice" ist ...). Ich werde meine Frage aktualisieren. – Slevin