Redux: Beste asynchrone serverseitige Ladetechnik?

Erstellt am 15. Juni 2015  ·  63Kommentare  ·  Quelle: reduxjs/redux

Zuallererst liebe ich diese Bibliothek und die Muster, die ihr verwendet. 👍👍

Ich versuche, Redux zu verwenden, um eine isomorphe App zu erstellen. Es funktioniert bisher wunderbar, außer dass ich herausfinden muss, wie ich warten muss, bis meine Stores geladen sind (auf dem Server), bevor ich den anfänglichen Seitenladevorgang zurücksende. Idealerweise sollte das Laden in den Stores selbst stattfinden, aber wenn ich dispatch(userActions.load()) , muss der Store den neuen Zustand zurückgeben (dh return { ...state, loading: true }; ), also kann er kein Versprechen an zurückgeben warten auf. dispatch() gibt die aus irgendeinem Grund übergebene Aktion zurück. Ich hätte wirklich gerne so etwas wie...

dispatch(someAsyncAction, successAction, failureAction) => Promise

...wo das Versprechen nicht aufgelöst wird, bis eine der beiden anderen Aktionen ausgeführt wird.

Könnte so etwas mit dem Middleware-Muster aktiviert werden?

Bin ich völlig daneben und gibt es bereits eine einfache Möglichkeit, dies zu tun?

Vielen Dank.

Hilfreichster Kommentar

// Middleware
export default function promiseMiddleware() {
  return (next) => (action) => {
    const { promise, types, ...rest } = action;
    if (!promise) {
      return next(action);
    }

    const [REQUEST, SUCCESS, FAILURE] = types;
    next({ ...rest, type: REQUEST });
    return promise.then(
      (result) => next({ ...rest, result, type: SUCCESS }),
      (error) => next({ ...rest, error, type: FAILURE })
    );
  };
}

// Usage
function doSomethingAsync(userId) {
  return {
    types: [SOMETHING_REQUEST, SOMETHING_SUCCESS, SOMETHING_FAILURE],
    promise: requestSomething(userId),
    userId
  };
}

Alle 63 Kommentare

Hey danke!

Idealerweise sollte die Verladung in den Filialen selbst erfolgen

Redux erzwingt, dass Stores vollständig synchron sind. Was Sie beschreiben, sollte stattdessen im Aktionsersteller passieren.

Ich _glaube_, dass es mit der Standard-Thunk-Middleware sogar möglich sein könnte. Ihr Action Creator würde so aussehen:

export function doSomethingAsync() {
  return (dispatch) => {
    dispatch({ type: SOMETHING_STARTED });

    return requestSomething().then(
      (result) =>  dispatch({ type: SOMETHING_COMPLETED, result }),
      (error) =>  dispatch({ type: SOMETHING_FAILED, error })
    );
  };
}

und Handhabung der tatsächlichen (granularen) Aktionen im Store.

Es ist auch möglich, eine benutzerdefinierte Middleware zu schreiben, um die Boilerplate zu entfernen.

Genius! Ich dachte, ich übersehe etwas Offensichtliches. Ich mag diese Trennung von _tun_ und _speichern_.

Ich freue mich darauf, diese Bibliothek wachsen zu sehen, obwohl sie schon ziemlich fertig ist. Prost, @gaearon!

Sie können auch eine benutzerdefinierte Middleware wie diese schreiben

export default function promiseMiddleware() {
  return (next) => (action) => {
    const { promise, ...rest } = action;
    if (!promise) {
      return next(action);
    }

    next({ ...rest, readyState: 'request' );
    return promise.then(
      (result) => next({ ...rest, result, readyState: 'success' }),
      (error) => next({ ...rest, error, readyState: 'failure' })
    );
  };
}

und verwenden Sie es anstelle der Standardeinstellung.

Auf diese Weise können Sie asynchrone Aktionsersteller wie schreiben

function doSomethingAsync(userId) {
  return {
    type: SOMETHING,
    promise: requestSomething(userId),
    userId
  };
}

und lassen sie sich in verwandeln

{ type: SOMETHING, userId: 2, readyState: 'request' }
{ type: SOMETHING, userId: 2, readyState: 'success' }
{ type: SOMETHING, userId: 2, readyState: 'failure' }

Oh, das ist auch schön, und mehr von dem, was ich im Sinn hatte, als ich die ursprüngliche Frage stellte. Ich kann nicht sagen, ob mir der Kompromiss gefällt, die Anzahl der Aktionskonstanten im Austausch für das Hinzufügen von if s zu reduzieren, um die readyState im Geschäft zu überprüfen. Ich denke, ich könnte es vorziehen, zusätzliche _SUCCESS und _FAILURE Versionen jeder Aktion zu haben, nur um zu vermeiden, dass ein if in ein case .

Trotzdem danke.

Ja das ist ganz deinem Geschmack überlassen. Sie könnten eine ähnliche Version haben, die types: { request: ..., success: ..., failure: ... } in Aktionen umwandelt. Das ist der Sinn, es zu einer Middleware zu machen, anstatt in die Bibliothek zu backen: Jeder hat seinen eigenen Geschmack bei diesen Dingen.

// Middleware
export default function promiseMiddleware() {
  return (next) => (action) => {
    const { promise, types, ...rest } = action;
    if (!promise) {
      return next(action);
    }

    const [REQUEST, SUCCESS, FAILURE] = types;
    next({ ...rest, type: REQUEST });
    return promise.then(
      (result) => next({ ...rest, result, type: SUCCESS }),
      (error) => next({ ...rest, error, type: FAILURE })
    );
  };
}

// Usage
function doSomethingAsync(userId) {
  return {
    types: [SOMETHING_REQUEST, SOMETHING_SUCCESS, SOMETHING_FAILURE],
    promise: requestSomething(userId),
    userId
  };
}

Oh Mann, ich liebe diese Lösung. So viel schöner, als die then() und zusätzliche Anrufe an dispatch() wie die erste von Ihnen vorgeschlagene Lösung. Hurra für Middleware!

Lass mich wissen, wie (und ob ;-) es funktioniert!
Wir haben benutzerdefinierte Middleware nicht wirklich viel getestet.

Du hast ein } weggelassen (das ist -1 Punkt 😀), aber es hat wunderbar funktioniert! Erstes Mal.

:+1:

@erikras Ich bin gespannt, wie Sie das Warten auf die Auflösung der Versprechen auf dem Server implementiert haben?

Dies ist nur Pseudocode, also fügen Sie das nirgendwo ein, aber ich verwende einen React-Router (dessen API sich so schnell ändert wie der von Redux) etwa so:

app.get('/my-app', (req, res) => {
  Router.run(routes, req.path, (error, initialState) => {
    Promise.all(initialState.components
      .filter(component => component.fetchData) // only components with a static fetchData()
      .map(component => {
        // have each component dispatch load actions that return promises
        return component.fetchData(redux.dispatch);
      })) // Promise.all combines all the promises into one
      .then(() => {
        // now fetchData() has been run on every component in my route, and the
        // promises resolved, so we know the redux state is populated
        res.send(generatePage(redux));
      });
  });
});

Klärt das etwas?

@ie

Zitiere dein Problem in Slack:

Ich habe einen Routenhandler mit

 static async routerWillRun({dispatch}) {
   return await dispatch(UserActions.fooBar());
 }

wobei UserActions.fooBar() ist:

export function fooBar() {
 return dispatch => {
   doAsync().then(() => dispatch({type: FOO_BAR}));
 };
}

dann gebe ich im Server-Rendern nach:

 yield myHandler.routerWillRun({dispatch: redux.dispatch});

aber es funktioniert nicht.

Ich denke, das Problem hier ist, dass Sie nichts von der verschachtelten Methode von fooBar .

Entfernen Sie entweder die Klammern:

export function fooBar() {
  return dispatch =>
    doAsync().then(() => dispatch({type: FOO_BAR}));
}

oder fügen Sie eine explizite return Anweisung hinzu:

export function fooBar() {
  return dispatch => {
    return doAsync().then(() => dispatch({type: FOO_BAR}));
  };
}

In jedem Fall ist es möglicherweise einfacher, eine benutzerdefinierte Promise-Middleware zu verwenden, wie oben vorgeschlagen.

@erikras In Bezug auf Ihren letzten Kommentar, in dem Sie die fetchData-Methode für initialState.components aufrufen (im Callback von Router.run), gibt das Objekt, von dem Sie die Komponentenreferenzen erhalten, nur die übereinstimmenden Routenhandler zurück. Was halten Sie davon, Komponenten zu erreichen, die möglicherweise kein angepasster Route-Handler sind, dh eine untergeordnete Komponente, die jedoch Daten abrufen müssen?

hier ist ein beispiel wovon ich rede

import React from 'react';
import Router from 'react-router';
import {Route, RouteHandler, DefaultRoute} from 'react-router';

//imagine Bar needs some data
const Bar = React.createClass({
  render(){
    return(
      <div>bar</div>);
  }
});

const Foo = React.createClass({
  render(){
    return (
      <div>
        foo
        <Bar/>
      </div>);
  }
});


const App = React.createClass({
  render(){
    return (
      <div>
        <RouteHandler />
      </div>
    );
  }
});

const routes = (
  <Route path="/" handler={App} name="App">
    <DefaultRoute handler={Foo} name="Foo"/>
  </Route>
);

Router.run(routes,'/',function(Root,state){
  console.log(state);
});

Ausgang:

{ path: '/',
  action: null,
  pathname: '/',
  routes: 
   [ { name: 'App',
       path: '/',
       paramNames: [],
       ignoreScrollBehavior: false,
       isDefault: false,
       isNotFound: false,
       onEnter: undefined,
       onLeave: undefined,
       handler: [Object],
       defaultRoute: [Object],
       childRoutes: [Object] },
     { name: 'Foo',
       path: '/',
       paramNames: [],
       ignoreScrollBehavior: false,
       isDefault: true,
       isNotFound: false,
       onEnter: undefined,
       onLeave: undefined,
       handler: [Object] } ],
  params: {},
  query: {} }

Sie haben keinen Zugriff auf Bar in Routes

@erikras Fantastisch! Das ist genau die Art von Route, die ich gehen möchte. Danke für das Teilen.

@iest Ich hoffe, dass das Wortspiel beabsichtigt war, "gehen Sie eine Route hinunter", indem Sie die passenden Routen durchlaufen. :-)

@mattybow Das stimmt. Wenn Sie _wirklich_ eine Komponente benötigen, die nicht in Ihren Routen enthalten ist, um etwas zu laden, dann besteht die einzige Möglichkeit darin, React.renderToString() einmal auszuführen (das Ergebnis zu verwerfen), alle Ihre Ladevorgänge in componentWillMount() durchzuführen und irgendwie Speichern Sie die Versprechen, während Sie gehen. Dies habe ich mit meiner eigenen selbst entwickelten Routing-Lösung getan, bevor react-router serverseitiges Rendering unterstützte. Ich würde vorschlagen, dass die Notwendigkeit, Nicht-Route-Komponenten zum Laden zu benötigen, ein Symptom für ein Konstruktionsproblem sein könnte. In den meisten Anwendungsfällen weiß eine Route, welche Daten ihre Komponenten benötigen.

@erikras
Haben Sie ein öffentliches Repo, um dort ein ganzes Beispiel für Ihre Lösung zu sehen?

@transedward Ich wünschte, ich hätte es getan, aber meine bisherigen Sachen mit der hier beschriebenen Methode sind noch sehr unreif. Es tut uns leid.

+1 für das fortgeschrittene isomorphe Beispiel
Ich liebe es, wohin das führt!

@transedward Hier ist ein Beispielprojekt mit all der https://github.com/erikras/react-redux-universal-hot-example/

@erikras Das ist großartig! Können Sie bitte PR einreichen, um sie zu dieser README- und React Hot Loader-Dokumentation "Starter-Kits"-Abschnitt hinzuzufügen?

Vielen Dank! PRs eingereicht.

@erikras Toll - Danke!

Nur eine Anmerkung, dass ich – basierend auf einigen der Ideen in diesem Gespräch – eine Middleware erstellt habe, um Versprechen zu handhaben: https://github.com/pburtchaell/redux-promise-middleware.

@pburtchaell Es gibt auch diese Bibliothek von @acdlite . https://github.com/acdlite/redux-promise

Zwei Gedanken dazu:

  1. Versprechen, die in Aktionen umgewandelt werden, können zusammen mit der Aktion weitergegeben und in den Redux Store gestellt werden.

Um zu wissen, ob Ihre Seite zum Rendern bereit ist, überprüfen Sie einfach, ob alle Zusagen erfüllt wurden. Verwenden Sie vielleicht eine Middleware, die alle durchlaufenden Promises miteinander verkettet und eine Promise für den Fall bereitstellt, dass keine ausstehenden Promises vorhanden sind.

  1. Wie wäre es, wenn Selektoren Aktionen für fehlende Daten aufrufen lassen?

Angenommen, Sie möchten Nachricht 3 rendern, dann rendert Ihr Nachrichtencontainer <Message id={3}> und der Nachrichtenselektor prüft, ob state.msgs[3] vorhanden ist, und sendet andernfalls ein Nachrichtenladeversprechen.

Kombinieren Sie also die beiden und die Komponenten würden automatisch ihre benötigten Daten auswählen und Sie wissen, wann sie fertig sind.

Ich bin mir ziemlich sicher, dass "nichts unserialisierbares in Speicher oder Aktionen legen". Es ist eine der Invarianten, die für mich sehr gut funktioniert hat (und zum Beispiel Zeitreisen erlaubt) und es muss _sehr_ zwingende Gründe geben, sie zu ändern.

Um zu wissen, ob Ihre Seite zum Rendern bereit ist, überprüfen Sie einfach, ob alle Zusagen erfüllt wurden. Verwenden Sie vielleicht eine Middleware, die alle durchlaufenden Promises miteinander verkettet und eine Promise für den Fall bereitstellt, dass keine ausstehenden Promises vorhanden sind.

Dafür muss man am Ende eigentlich keine Versprechungen machen, und das gefällt mir. Der Unterschied besteht darin, dass Rohaktionen am Ende der Versandkette keine Versprechen enthalten. Diese werden stattdessen von der besagten Middleware „gesammelt“.

Eine Sache, die ich oft mache, wenn ich mit Promises arbeite, ist, einen Verweis auf ein Promise beizubehalten, damit ich, wenn andere Anfragen für dasselbe eingehen, einfach dasselbe Promise zurücksende, um eine Entprellung zu ermöglichen. Ich lösche dann die Referenz einige Zeit nach Abschluss der Zusage, um konfigurierbares Caching bereitzustellen.

Ich muss wirklich anfangen, Redux in echten Apps zu verwenden, weil ich mich frage, was ich mit diesen Referenzen in Redux machen soll. Ich möchte sie irgendwie in den Store stecken, um den actionCreator zustandslos zu machen (oder zumindest den Zustand explizit zu machen). Das Versprechen über eine Aktion zu übergeben ist eine nette Möglichkeit, es zu exportieren, aber dann müssen Sie es später irgendwie zurückbekommen. Hmmm.

Ich freue mich schon sehr auf die Antwort zum letzten Punkt von @wmertens .
Das Vermeiden mehrerer gleichzeitiger Aufrufe (nichts tun, wenn etwas ansteht) ist ein häufiger Anwendungsfall, bei dem Sie nicht sicher sind, wie Sie dies am besten beantworten können (weil Sie nicht über einen actionCreator auf den Status des Shops zugreifen können).

Soll der actionCreator-Benutzer (die Komponente) jedes Mal im Zustand prüfen, ob er die Aktion versenden kann oder nicht? Sie müssen es dann jedes Mal tun. Vielleicht müssen Sie Ihre eigene @connect- Anmerkung einführen, die dies

weil Sie von einem actionCreator nicht auf den Status des Shops zugreifen können

Wieso den? Sie können es gut tun, wenn Sie Thunk-Middleware verwenden .

function doSomething() {
  return { type: 'SOMETHING' };
}

function maybeDoSomething() {
  return function (dispatch, getState) {
    if (getState().isFetching) {
      return;
    }

    dispatch(doSomething());
  };
}

store.dispatch(maybeDoSomething());

das löst es!
Ich dachte eine Zeitlang (ohne Grund), dass der Zugriff auf den Zustand in actionCreator eine schlechte Praxis ist^^ da der Aufrufer der Aktionserstellerin auf den Zustand zugreifen kann, verstehe ich nicht, warum der actionCreator nicht sollte, also ja, cool, wir können das machen :)

Danke @gaearon

@gaearon , ist diese Technik der Verwendung von Thunk und unterschiedlichen Aktionen http://gaearon.github.io/redux/docs/api/applyMiddleware.html Ihrer obigen Antwort vorzuziehen:

Sie können auch eine benutzerdefinierte Middleware wie diese schreiben

export default function promiseMiddleware() {
  return (next) => (action) => {
    const { promise, ...rest } = action;
    if (!promise) {
      return next(action);
    }

    next({ ...rest, readyState: 'request' );
    return promise.then(
      (result) => next({ ...rest, result, readyState: 'success' }),
      (error) => next({ ...rest, error, readyState: 'failure' })
    );
  };
}

und verwenden Sie es anstelle der Standardeinstellung.
Auf diese Weise können Sie asynchrone Aktionsersteller wie schreiben

function doSomethingAsync(userId) {
  return {
    type: SOMETHING,
    promise: requestSomething(userId),
    userId
  };
}

und lassen sie sich in verwandeln

{ type: SOMETHING, userId: 2, readyState: 'request' }
{ type: SOMETHING, userId: 2, readyState: 'success' }
{ type: SOMETHING, userId: 2, readyState: 'failure' }

Ebenfalls,

Ich denke für den letzten Teil meintest du:

{ type: SOMETHING, userId: 2, readyState: 'request' }
{ type: SOMETHING, userId: 2, result, readyState: 'success' }
{ type: SOMETHING, userId: 2, error, readyState: 'failure' }

Ich vermute, es hängt nur davon ab, ob man separate Aktionen für die success oder failure Rückrufe des Versprechens erstellen möchte, anstatt die automatisch generierte zu verwenden.

In deinem Denkbeispiel:

function makeASandwichWithSecretSauce(forPerson) {

  // Invert control!
  // Return a function that accepts `dispatch` so we can dispatch later.
  // Thunk middleware knows how to turn thunk async actions into actions.

  return function (dispatch) {
    return fetchSecretSauce().then(
      sauce => dispatch(makeASandwich(forPerson, sauce)),
      error => dispatch(apologize('The Sandwich Shop', forPerson, error))
    );
  };
}

dann wäre es nicht unbedingt sinnvoll, einen generischen Fehlerrückruf für das Fehlschlagen des Abrufens einer geheimen Soße zu verwenden, da es unterschiedliche Umstände für das Abrufen der Soße geben kann.

Dadurch kann ich das Thunk-Modell etwas flexibler sehen.

Vielleicht sind so etwas wie das Logging oder vielleicht sogar das Umschalten eines "Async in Progress Busy Indicator" geeignetere Beispiele für Middleware?

@justin808

Beide sind gut. Wählen Sie aus, was für Sie weniger ausführlich ist und was für Ihr Projekt besser funktioniert. Mein Vorschlag ist, mit der Verwendung von Thunks zu beginnen, und wenn sich einige Muster wiederholen, extrahieren Sie sie in benutzerdefinierte Middleware. Sie können sie auch mischen.

Ich habe einen ActionStore erstellt, um den Status der ausgelösten Aktionen (Laden, erfolgreich, fehlgeschlagen) vom anderen Status zu trennen. Ich weiß aber nicht, ob dies gegen die Basis von Redux/Flux verstößt. Ich habe in Stackoverflow darüber gepostet.

@gabrielgiussi Ich denke, https://github.com/acdlite/redux-promise kann auch erreichen, was Sie wollen, ohne dass Sie Versprechen im Staat speichern müssen. Der Zustand soll jederzeit serialisierbar sein.

@wmertens danke für die Ratschläge. Ich werde mir das Repo ansehen, aber warum sollte mein Status nicht serialisierbar sein? Oder sagst du es nur, damit ich es zur Kenntnis nehme?

@gabrielgiussi Ich habe nicht genau
Versprechen oder Funktionen im Laden platzieren. Auf jeden Fall das Projekt
sollte meiner Meinung nach auch gut funktionieren.

Am Mo, 10.08.2015, 19:15 Uhr schrieb gabrielgiussi [email protected] :

@wmertens https://github.com/wmertens danke für die Beratung. ich werde
werfen Sie einen Blick auf das Repo, aber warum sollte mein Status nicht serialisierbar sein? Oder du
Sag es nur, damit ich es zur Kenntnis nehme?


Antworten Sie direkt auf diese E-Mail oder zeigen Sie sie auf GitHub an
https://github.com/gaearon/redux/issues/99#issuecomment -129531103.

Wout.
(auf dem Handy getippt, Entschuldigung der Knappheit)

Tatsächlich habe ich im Store benutzerdefinierte Action-Objekte platziert, sie sind einfach unveränderlich. Record mit einfachen Attributen (ID, State, Payload) und einem Aktionsauslöser, der ein Promise erstellt und zurückgibt, also stelle ich keine Promises in den Store. Aber wahrscheinlich mache ich woanders was kaputt, je. Danke @wmertens.

@gabrielgiussi

und einen Aktionsauslöser, der ein Versprechen erstellt und zurückgibt

Versetzen Sie keine Funktionen oder irgendetwas anderes, das nicht serialisierbar ist, in den Zustand.

Es tut uns leid. Ich habe versucht zu sagen

und ein Funktionstrigger, der ein Promise erstellt und zurückgibt

Was ich eigentlich in den Store stelle, ist ein Action-Objekt (der Name war nicht der beste):

export default class Action extends Immutable.Record({state: 'idle', api: null, type: null, payload: null, id: null}){
load(){
  return this.set('state','loading');
}

succeed(){
  return this.set('state','succeeded');
}

fail(){
  return this.set('state','failed');
}

ended(){
  return this.get('state') != 'loading' && this.get('state') != 'idle';
}

endedWithSuccess(){
  return this.get('state') == 'succeeded';
}

endedWithFailure(){
  return this.get('state') == 'failed';
}

trigger() {
  return (dispatch) => {
    dispatch({type: this.get('type') + '_START', action: this});
    let payload = this.get('payload');
    this.get('api').call({dispatch,payload}).then((result) => {
      dispatch({type: this.get('type') + '_SUCCESS',id: this.get('id'), result: result.result});
    }).catch((result) => {
        dispatch({type: this.get('type') + '_FAIL',id: this.get('id'), result: result.result});
      });
  }
}
}

Ich habe eine Bibliothek zur Lösung dieses Problems erstellt (siehe #539). Sie funktioniert, indem Middleware-Versprechen für ausstehende Aktionen zurückgegeben werden und darauf gewartet wird, dass all diese Versprechen gelöst werden.

@gaearon diesen Code haben Sie geschrieben https://github.com/rackt/redux/issues/99#issuecomment -112212639,

Ist dies etwas, das in der Redux-Bibliothek enthalten ist oder muss ich es manuell erstellen? Entschuldigung, wenn dies eine neue Frage ist, die gerade erst in React / Flux (Redux) einsteigt. Habe gerade dieses Tutorial gestartet https://github.com/happypoulp/redux-tutorial

@banderson5144

Es ist nicht enthalten. Es ist nur dazu da, Ihnen eine Vorstellung davon zu geben, was Sie tun können – aber Sie können es auch anders machen.
Etwas Ähnliches wurde als https://github.com/pburtchaell/redux-promise-middleware veröffentlicht.

Danke für diese nützliche Info. Ich wollte dir beim Zurücksetzen eines Ladens den Kopf zerbrechen -

  • Sie setzen den Store-Status für einen neuen Benutzer zurück
  • Sie warten, bis einige asynchrone Aktionen abgeschlossen sind, bevor Sie dem Benutzer den Store und die HTML-Datei geben
  • Eine zuvor ausgeführte asynchrone Aktion für einen anderen Benutzer wird abgeschlossen und Ihr Geschäft wird verschmutzt

Wie habt ihr das gelöst/ habt ihr eine Idee wie man das macht? Würde stattdessen nur ein neuer Shop für jeden Benutzer funktionieren?

Meinst du Server-Rendering? Erstellen Sie bei jeder Anfrage einen neuen Shop. Wir haben eine Anleitung zum Server-Rendering in der Dokumentation.

Danke das werde ich machen

Nach dem Versuch zu verstehen…

Ist das zu naiv? (niemand sonst scheint das zu tun - denke ich)

// server.js
app.use(function (req, res) {
    match({…}, function (error, redirectLocation, renderProps) {
        …

        if (renderProps) {
            const store = configureStore();

            const promises = renderProps.components.map(function (component, index) {
                if (typeof component.fetchData !== 'function') {
                    return false;
                }

                return component.fetchData(store.dispatch);
            });

            Promise.all(promises).then(function () {
                res.status(200).send(getMarkup(store, renderProps));
            });
        }
    })
});
// home.js
export class Home extends Component {
    static fetchData() {
        return Promise.all([
            dispatch(asyncAction);
        ]);
    },

    componentDidMount() {
        const { dispatch } = this.props;

        Home.fetchData(dispatch);
    }
}

export default connect()(Home);
// action.js
export function asyncAction() {
    return (dispatch, getState) => {
        dispatch(request);

        return fetch(…)
            .then(response => response.json())
            .then(data => dispatch(requestSuccess(data)))
        ;
    }
}

Ich habe versucht , auch eine Lösung für @mattybow ‚s Frage , um herauszufinden , https://github.com/rackt/redux/issues/99#issuecomment -112.980.776 (verschachtelte Komponenten Abrufen von Daten Verwaltung), aber kein solcher Erfolg (nicht sicher, wie man Versprechen von componentWillMount einsammelt).

@chemoish Ich versuche auch, mich mit dem serverseitigen Rendering mit Reagieren und Reduxen zu beschäftigen. Das Beispiel in der Dokumentation handhabt diesen Anwendungsfall nicht sehr gut. Ich möchte nicht jede API-Anfrage auf dem Server mit meinen Komponenten neu spezifizieren und koppeln. Die Komponente sollte nur angeben, wie die benötigten Daten abgerufen werden (die der Server dann abholen soll).

Ihre Lösung sieht ganz gut aus, um das zu erreichen. Hat das bei dir geklappt? Vielen Dank

Bearbeiten: Ich habe Recht, dass "componentDidMount" auf dem Client nicht erneut ausgelöst wird, wenn es auf dem Server gerendert wird?

@ms88privat Ich habe noch nicht viel Feedback zur Lösung erhalten und ihre Grenzen nicht getestet.

Die oben gestellte Lösung erfordert jedoch, dass jede Seite die Daten für alle ihre untergeordneten Komponenten kennt. Ich habe mich nicht eingehender damit befasst, dass sich verschachtelte Komponenten selbst um die Datenverwaltung kümmern (aufgrund des Sammelns verschachtelter Versprechen).

Es scheint das zu tun, was Sie erwarten würden, also ist es für mich jetzt gut genug.


componentDidMount wird erneut ausgelöst (Siehe https://facebook.github.io/react/docs/component-specs.html#mounting-componentdidmount). Sie können diese oder eine andere Lebenszyklusmethode verwenden, die Ihren Anforderungen entspricht.

Ich umgehe dies, indem ich die Ausführung des fetch Codes verhindere, wenn der Store bereits voll ist (oder welche Geschäftslogik Sie für richtig halten).

Unter https://github.com/reactjs/redux/blob/master/examples/async/actions/index.js#L47 finden Sie eine Vorstellung davon, wovon ich spreche.

@chemoish

Ich umgehe dies, indem ich die Ausführung des Abrufcodes verhindere, wenn der Speicher bereits voll ist

Okay, das habe ich verstanden. Vielen Dank.

Die oben gestellte Lösung erfordert jedoch, dass jede Seite die Daten für alle ihre untergeordneten Komponenten kennt.

Vielleicht habe ich Ihre Lösung falsch verstanden, aber ist dies nicht eine notwendige Voraussetzung, unabhängig vom serverseitigen Rendering? (zB sollte es den gleichen Zustand rendern, wenn ich auf der aktuellen Route aktualisiere, auch wenn es ein SPA ist)

Das wäre es, aber Sie möchten vielleicht, dass eine verschachtelte Komponente ihren eigenen Datenabruf verwaltet, aus welchem ​​Grund auch immer.

Zum Beispiel eine Komponente, die auf vielen Seiten wiederholt wird, aber jede Seite hat nicht viele Datenabrufanforderungen.

@chemoish Ich bin mir nicht sicher, ob wir auf derselben Seite sind. Lassen Sie mich versuchen, meinen Standpunkt zu erklären.

Zum Beispiel habe ich drei verschachtelte Komponenten:

  • Komponente1 (statischer dataFetch1)

    • Komponente2 (statischer dataFetch2)

    • Komponente3 (statischer dataFetch3)

Jeder von ihnen hat seine eigenen "componentDidMount"-Methoden mit eigenen dataFetching-Deklarationen (Verteilen von Aktionen über seine statische dataFetching-Methode).

Wenn ich kein serverseitiges Rendering habe und die aktuelle URL aktualisiere, werden meine Komponenten gemountet und alle Aktionen ausgelöst, die erforderlich sind, um alle erforderlichen Daten anschließend zu laden.

Beim serverseitigen Rendering extrahieren Ihre match Funktion und renderProps alle drei Komponenten, sodass ich auf alle statischen dataFetching-Methoden zugreifen kann, die es mir dann ermöglichen, alle benötigten Daten abzurufen das anfängliche serverseitige Rendering?

Haben Sie einen Verweis auf Ihre match function aus Ihrem bereitgestellten Beispiel? Danke.

@ms88privat renderProps.components ist ein Array von Routerkomponenten , es geht nicht tiefer. @chemoish bedeutete, dass Sie mit seiner Implementierung die Datenabrufanforderungen für tiefere Komponenten nicht beschreiben können.

@DominicTobias thx, hast du eine Lösung für dieses Problem? Gibt es eine Möglichkeit alle verschachtelten Komponenten zu bekommen?

Vielleicht kann das helfen? https://github.com/gaearon/react-side-effect
Wird verwendet, um alle Meta-Tags von verschachtelten Elementen zu sammeln: https://github.com/nfl/react-helmet

Entschuldigung, dass ich diese Diskussion erneut angestoßen habe, aber ich habe vor kurzem das gleiche Problem beim Vorfüllen des Status mit asynchroner Aktion gehabt.

Ich kann sehen, dass @erikras sein Boilerplate-Projekt nach redux-async-connect verschoben hat. Ich frage mich, ob jemand eine andere Lösung gefunden hat?

@vtambourine Ich habe mir https://github.com/markdalgleish/redial angesehen, was sehr hilfreich ist

Ja, ich habe es durchgesehen. Aber ich habe nicht verstanden, wie man Daten sicherstellt
Das Abrufen von Hooks wird nach der Neuinitialisierung des Codes auf n . nicht zum zweiten Mal ausgeführt
Klient.
Am т, 18 марта 2016 г. um 22:54 Uhr, Sean Matheson [email protected]
schrieb:

@vtambourine https://github.com/vtambourine Ich habe mir angesehen
https://github.com/markdalgleish/redial was sehr hilfreich ist


Sie erhalten dies, weil Sie erwähnt wurden.
Antworten Sie direkt auf diese E-Mail oder zeigen Sie sie auf GitHub an
https://github.com/reactjs/redux/issues/99#issuecomment -198517067

Auch gespannt, ob jemand eine stabile Lösung für diese Herausforderung gefunden hat. Ich liebe @erikras ‚s vorformulierten, sondern als @vtambourine erwähnt, hat es sich bewegt Redux-Asynchron-connect , die wie es scheint , kann keine stabile langfristige Lösung sein: # 81 Redux-Asynchron-connect tot? .

@vtambourine gibt es eine Gabel , das ist erhältlich bei https://github.com/makeomatic/redux-connect und ist gut gepflegt. Es hat eine ähnliche API mit ein paar Änderungen, sieh es dir an, wenn du interessiert bist

Für diejenigen, die an einer Redux-Lösung mit Middleware interessiert sind, wie von @gaearon erwähnt,

https://github.com/peter-mouland/react-lego-2016#redux -with-promise-middleware

Wie kann man Action Creator mit diesem Ansatz testen?

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen

Verwandte Themen

caojinli picture caojinli  ·  3Kommentare

dmitry-zaets picture dmitry-zaets  ·  3Kommentare

amorphius picture amorphius  ·  3Kommentare

captbaritone picture captbaritone  ·  3Kommentare

CellOcean picture CellOcean  ·  3Kommentare