Redux: Meilleure technique de chargement asynchrone côté serveur ?

Créé le 15 juin 2015  ·  63Commentaires  ·  Source: reduxjs/redux

Tout d'abord, j'adore cette bibliothèque et les modèles que vous utilisez. ??

J'essaie d'utiliser redux pour créer une application isomorphe. Cela fonctionne à merveille jusqu'à présent, sauf que je dois trouver comment attendre que mes magasins se soient chargés (sur le serveur) avant de renvoyer le chargement initial de la page. Idéalement, le chargement devrait avoir lieu dans les magasins eux-mêmes, mais lorsque j'appelle dispatch(userActions.load()) , le magasin doit renvoyer le nouvel état (c'est- return { ...state, loading: true }; dire dispatch() renvoie l'action qui lui a été transmise pour une raison quelconque. J'aimerais vraiment quelque chose comme...

dispatch(someAsyncAction, successAction, failureAction) => Promise

... où la promesse ne se résout pas tant que l'une des deux autres actions n'est pas envoyée.

Est-ce le genre de chose qui pourrait être activé avec le pattern middleware ?

Suis-je totalement hors de propos et existe-t-il déjà un moyen simple de le faire ?

Merci.

Commentaire le plus utile

// 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
  };
}

Tous les 63 commentaires

Hey, merci!

Idéalement, le chargement devrait avoir lieu dans les magasins eux-mêmes

Redux impose que les magasins soient complètement synchrones. Ce que vous décrivez devrait plutôt se produire dans le créateur d'action.

Je pense que cela peut même être possible avec le middleware thunk par défaut. Votre créateur d'action ressemblerait à :

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

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

et gérer les actions réelles (granulaires) dans le magasin.

Il est également possible d'écrire un middleware personnalisé pour supprimer le passe-partout.

Génie! J'ai pensé que j'oubliais quelque chose d'évident. J'aime cette séparation entre _faire_ et _stocker_.

J'ai hâte de voir cette bibliothèque grandir, même si elle est déjà assez complète. Salut, @gaearon !

Vous pouvez également écrire un middleware personnalisé comme celui-ci

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' })
    );
  };
}

et l'utiliser à la place de celui par défaut.

Cela vous permettra d'écrire des créateurs d'action asynchrones comme

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

et les faire se transformer en

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

Ooh, c'est bien aussi, et plus de ce que j'avais en tête quand j'ai posé la question initiale. Je ne peux pas dire si j'aime le compromis consistant à réduire le nombre de constantes d'action en échange de l'ajout de if pour vérifier les readyState à l'intérieur du magasin. Je pense que je préférerais peut-être avoir des versions _SUCCESS et _FAILURE supplémentaires de chaque action juste pour éviter de mettre un if dans un case .

Merci quand même.

Oui, c'est tout à fait à votre goût. Vous pourriez avoir une version similaire qui transforme types: { request: ..., success: ..., failure: ... } en actions. C'est l'intérêt d'en faire un middleware au lieu d'en faire une bibliothèque : chacun a son propre goût pour ces choses.

// 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 mec, j'adore cette solution. Tellement plus agréable que d'avoir le then() et des appels supplémentaires à dispatch() comme la première solution que vous avez proposée. Hourra pour le middleware !

Dites-moi comment (et si ;-) ça marche !
Nous n'avons pas vraiment beaucoup testé les middlewares personnalisés.

Vous avez omis un } (ça fait -1 point 😀), mais ça a marché à merveille ! Première fois.

:+1:

@erikras Je suis curieux de savoir comment vous avez implémenté l'attente de la résolution des promesses sur le serveur ?

Ce n'est qu'un pseudo-code, alors ne le collez nulle part, mais j'utilise react-router (dont l'API change aussi vite que celle de redux) quelque chose comme ceci :

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));
      });
  });
});

Est-ce que ça clarifie quelque chose ?

@iest

Citant votre problème dans Slack :

J'ai un gestionnaire de route avec

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

UserActions.fooBar() est :

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

puis dans le rendu du serveur, je cède :

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

mais ça ne marche pas.

Je pense que le problème ici est que vous ne retournez rien de la méthode imbriquée de fooBar .

Soit enlevez les accolades :

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

ou ajoutez une instruction return explicite :

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

Quoi qu'il en soit, il peut être plus facile d'utiliser un middleware de promesse personnalisé comme suggéré ci-dessus.

@erikras En initialState.components (dans le rappel de Router.run), l'objet dont vous obtenez les références de composant ne renvoie que les gestionnaires de route correspondants. Que pensez-vous de l'atteinte de composants qui pourraient ne pas être un gestionnaire de route correspondant, c'est-à-dire un composant enfant, mais qui a besoin de récupérer des données ?

voici un exemple de ce dont je parle

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);
});

sortir:

{ 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: {} }

Vous n'aurez pas accès au bar dans les itinéraires

@erikras Fantastique ! C'est exactement le genre d'itinéraire que je veux emprunter. Merci d'avoir partagé.

@iest J'espère que ce jeu de mots était intentionnel, "suivre une route" en itérant à travers les routes correspondantes. :-)

@mattybow C'est vrai. Si vous avez vraiment besoin d'un composant qui n'est pas dans vos routes pour charger quelque chose, alors la seule option est d'exécuter React.renderToString() une fois (en ignorant le résultat), de faire tout votre chargement dans componentWillMount() , et d'une manière ou d'une autre gardez les promesses au fur et à mesure. C'est ce que je faisais avec ma propre solution de routage maison avant que react-router supporte le rendu côté serveur. Je suggérerais que le fait d'avoir besoin de composants sans routage pour effectuer le chargement pourrait être le symptôme d'un problème de conception. Dans la plupart des cas d'utilisation, une route sait de quelles données ses composants auront besoin.

@erikras
avez-vous un dépôt public pour voir un exemple complet de votre solution là-bas ?

@transedward J'aurais aimé le faire, mais mes affaires jusqu'à présent en utilisant la méthode que j'ai détaillée ici sont encore très immatures. Désolé.

+1 sur l'exemple isomorphe avancé
J'aime où cela va!

@transedward Voici un exemple de projet avec toutes les technologies de pointe que j'ai bricolées. https://github.com/erikras/react-redux-universal-hot-example/

@erikras C'est génial ! Pouvez-vous s'il vous plaît soumettre PR pour l'ajouter à cette section "kits de démarrage" de la documentation README et React Hot Loader ?

Merci! PRs soumis.

@erikras Super - Merci !

Juste une note que, sur la base de certaines des idées de cette conversation, j'ai créé un middleware pour gérer les promesses : https://github.com/pburtchaell/redux-promise-middleware.

@pburtchaell Il y a aussi cette bibliothèque de @acdlite . https://github.com/acdlite/redux-promise

Deux réflexions à ce sujet :

  1. Les promesses qui sont converties en actions peuvent être transmises avec l'action et placées dans le Redux Store.

Ensuite, pour savoir si votre page est prête à être rendue, vérifiez simplement si toutes les promesses sont terminées. Utilisez peut-être un middleware qui enchaîne toutes les promesses en cours et fournit une promesse lorsqu'il n'y a pas de promesses en attente.

  1. Que diriez-vous de laisser les sélecteurs appeler des actions pour les données manquantes ?

Supposons que vous souhaitiez afficher le message 3, votre conteneur de messages affichera <Message id={3}> et le sélecteur de message vérifiera si state.msgs[3] existe et sinon, enverra une promesse de chargement de message.

Donc, combinez les deux et les composants sélectionneront automatiquement les données nécessaires et vous saurez quand ils auront terminé.

Je suis à peu près sûr de "ne mettez rien d'unsérialisable dans le magasin ou les actions". C'est l'un des invariants qui a très bien fonctionné pour moi (et a permis de voyager dans le temps, par exemple) et il doit y avoir des raisons _très_ impérieuses d'envisager de le changer.

Ensuite, pour savoir si votre page est prête à être rendue, vérifiez simplement si toutes les promesses sont terminées. Utilisez peut-être un middleware qui enchaîne toutes les promesses en cours et fournit une promesse lorsqu'il n'y a pas de promesses en attente.

En fait, cela ne nécessite pas de mettre des promesses dans le magasin à la fin, et j'aime ça. La différence est qu'à la fin de la chaîne d'expédition, les actions brutes ne contiennent aucune promesse. Ceux-ci sont "collectés" par ledit middleware à la place.

Une chose que je fais souvent lorsque je travaille avec des promesses est de garder une référence à une promesse autour de sorte que lorsque d'autres demandes pour la même chose arrivent, je renvoie simplement la même promesse, fournissant un anti-rebond. Je supprime ensuite la référence quelque temps après la fin de la promesse, fournissant une mise en cache configurable.

Je dois vraiment commencer à utiliser Redux dans de vraies applications, car je me demande quoi faire de ces références dans Redux. Je veux en quelque sorte les coller dans le magasin pour rendre l'actionCreator sans état (ou au moins rendre l'état explicite). Passer la promesse via une action est un bon moyen de l'exporter, mais vous devrez ensuite la récupérer d'une manière ou d'une autre. Hmmm.

J'attends vraiment avec impatience la réponse au dernier point de @wmertens .
Éviter plusieurs appels simultanés (ne rien faire si quelque chose est en attente) est un cas d'utilisation fréquent, je ne sais pas quelle est la meilleure façon d'y répondre (car vous ne pouvez pas accéder à l'état du magasin à partir d'un actionCreator).

L'utilisateur actionCreator (le composant) doit-il vérifier à chaque fois dans l'état s'il peut ou non envoyer l'action ? Vous devez alors le faire à chaque fois. Peut-être que vous devez introduire votre propre annotation @connect qui enveloppe cela ?

car vous ne pouvez pas accéder à l'état du magasin à partir d'un actionCreator

Pourquoi? Vous pouvez le faire très bien si vous utilisez un middleware thunk .

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

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

    dispatch(doSomething());
  };
}

store.dispatch(maybeDoSomething());

ça le résout !
J'ai pensé pendant un certain temps (sans raison) qu'accéder à l'état dans actionCreator était une mauvaise pratique^^ puisque l'appelant du créateur d'action peut accéder à l'état, je ne vois pas pourquoi l'actionCreator ne devrait pas, alors ouais cool on peut faire ça :)

Merci @gaearon

@gaearon , cette technique d'utilisation de Thunk et d'actions distinctes http://gaearon.github.io/redux/docs/api/applyMiddleware.html est-elle préférable à votre réponse ci-dessus :

Vous pouvez également écrire un middleware personnalisé comme celui-ci

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' })
    );
  };
}

et l'utiliser à la place de celui par défaut.
Cela vous permettra d'écrire des créateurs d'action asynchrones comme

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

et les faire se transformer en

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

Aussi,

Je pense que pour la dernière partie, tu voulais dire :

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

Je suppose que cela dépend si l'on veut créer des actions séparées pour les rappels success ou failure de la promesse, plutôt que d'utiliser celle générée automatiquement.

Dans votre exemple de réflexion :

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))
    );
  };
}

alors il n'aurait pas nécessairement de sens d'avoir un rappel d'erreur générique pour un échec de récupération d'une sauce secrète, car il pourrait y avoir différentes circonstances pour récupérer la sauce.

Ainsi, je peux voir le modèle thunk un peu plus flexible.

Peut-être que quelque chose comme la journalisation ou peut-être même le basculement d'un "indicateur d'occupation asynchrone en cours" sont des exemples plus appropriés de middleware ?

@justin808

Les deux vont bien. Choisissez ce qui est moins verbeux pour vous et ce qui fonctionne le mieux pour votre projet. Ma suggestion est de commencer par utiliser des thunks, et si vous voyez un motif se répéter, extrayez-le dans un middleware personnalisé. Vous pouvez aussi les mélanger.

J'ai créé un ActionStore pour séparer l'état des actions déclenchées (chargement, succès, échec) de l'autre état. Mais je ne sais pas si cela va à l'encontre de la base de Redux/Flux. J'ai posté dans stackoverflow à ce sujet.

@gabrielgiussi Je pense que https://github.com/acdlite/redux-promise peut également réaliser ce que vous voulez sans que vous ayez à stocker des promesses dans l'état. L'état est censé être sérialisable à tout moment.

@wmertens merci pour le conseil. Je vais jeter un œil au repo mais pourquoi mon état ne serait pas sérialisable ? Ou tu le dis seulement pour que j'en prenne note ?

@gabrielgiussi je n'ai pas regardé de très près mais il me semblait que tu l'étais
mettre des promesses ou des fonctions dans le magasin. En tout cas, ce projet
devrait aussi bien fonctionner je pense.

Le lundi 10 août 2015, 19:15 gabrielgiussi [email protected] a écrit :

@wmertens https://github.com/wmertens merci pour le conseil. je le ferai
jetez un oeil au repo mais pourquoi mon état ne serait pas sérialisable ? Ou toi
le dire seulement pour que je prenne note?

-
Répondez directement à cet e-mail ou consultez-le sur GitHub
https://github.com/gaearon/redux/issues/99#issuecomment-129531103 .

Wout.
(tapé sur mobile, excusez le laconisme)

En fait, dans le magasin, je mets des objets Action personnalisés, ils sont simplement immuables. Enregistrez avec des attributs simples (id, état, charge utile) et un déclencheur d'action qui crée et renvoie une promesse, donc je ne mets pas de promesses dans le magasin. Mais je casse probablement quelque chose ailleurs, je. Merci @wmertens.

@gabrielgiussi

et un déclencheur d'action qui crée et renvoie une promesse

Ne mettez pas de fonctions ou quoi que ce soit d'autre qui ne soit pas sérialisable, dans l'état.

Désolé. j'ai essayé de dire

et un déclencheur de fonction qui crée et renvoie une promesse

Ce que je mets en fait dans le magasin est un objet Action (le nom n'était pas le meilleur):

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});
      });
  }
}
}

J'ai créé une bibliothèque pour résoudre ce problème (voir #539) cela fonctionne en ayant des promesses de retour de middleware pour les actions en attente, et en attendant que toutes ces promesses soient résolues.

@gaearon ce code que vous avez écrit https://github.com/rackt/redux/issues/99#issuecomment -112212639,

Est-ce quelque chose qui est inclus dans la bibliothèque redux ou est-ce quelque chose que je dois créer manuellement ? Désolé s'il s'agit d'une nouvelle question, je viens juste d'entrer dans React / Flux (Redux). Je viens de commencer ce tutoriel https://github.com/happypoulp/redux-tutorial

@banderson5144

Ce n'est pas inclus. Il est juste là pour vous donner une idée de ce que vous pouvez faire, mais vous êtes libre de le faire différemment.
Quelque chose de similaire a été publié sous le nom https://github.com/pburtchaell/redux-promise-middleware.

Merci pour ces infos utiles. Je voulais te renseigner sur la réinitialisation d'un magasin -

  • Vous réinitialisez l'état du magasin pour un nouvel utilisateur
  • Vous attendez que certaines actions asynchrones se terminent avant de donner à l'utilisateur le magasin et le html
  • Une action asynchrone en cours d'exécution pour un autre utilisateur se termine et votre magasin est pollué

Comment avez-vous résolu cela / avez-vous des idées sur la façon de faire ? Un nouveau magasin pour chaque utilisateur fonctionnerait-il à la place ?

Parlez-vous du rendu du serveur ? Créez une nouvelle boutique à chaque demande. Nous avons un guide pour le rendu du serveur dans la doc.

Merci je vais faire ça

Après avoir essayé de comprendre...

Est-ce trop naïf ? (personne d'autre ne semble le faire, je pense)

// 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)))
        ;
    }
}

J'ai aussi essayé de trouver une solution pour la question de @mattybow https://github.com/rackt/redux/issues/99#issuecomment -112980776 (composants imbriqués gestion des données) allant chercher, mais pas un tel succès (était pas savoir comment collecter des promesses de componentWillMount ).

@chemoish J'essaie également de comprendre le rendu côté serveur avec React et Redux. L'exemple de la documentation ne gère pas très bien ce cas d'utilisation. Je ne veux pas spécifier et coupler à nouveau chaque requête API sur le serveur avec mes composants. Le composant doit uniquement spécifier comment obtenir les données nécessaires (que le serveur doit ensuite récupérer).

Vos solutions semblent assez bonnes pour y parvenir. Cela a-t-il fonctionné pour vous ? Merci

Edit : j'ai raison de dire que « componentDidMount » ne se déclenche pas à nouveau sur le client lorsqu'il est rendu sur le serveur ?

@ ms88privat Je n'ai pas encore eu beaucoup de retours sur la solution, ni testé ses limites.

Cependant, la solution posée ci-dessus nécessite que chaque page connaisse les données de tous ses composants enfants. Je n'ai pas approfondi la question de savoir si les composants imbriqués s'inquiètent de la gestion des données eux-mêmes (en raison de la collecte de promesses imbriquées).

Il semble faire ce que vous attendez, donc c'est assez bon pour moi pour le moment.


componentDidMount sera à nouveau déclenché (voir https://facebook.github.io/react/docs/component-specs.html#mounting-componentdidmount). Vous pouvez utiliser cette méthode de cycle de vie ou une autre qui convient à vos besoins.

Je contourne cela en empêchant le code fetch de s'exécuter si le magasin est déjà plein (ou quelle que soit la logique métier que vous jugez appropriée).

Examinez https://github.com/reactjs/redux/blob/master/examples/async/actions/index.js#L47 pour avoir une idée de ce dont je parle.

@chemoish

Je contourne cela en empêchant le code de récupération de s'exécuter si le magasin est déjà plein

Ok, j'ai compris. Merci.

Cependant, la solution posée ci-dessus nécessite que chaque page connaisse les données de tous ses composants enfants.

J'ai peut-être mal lu votre solution, mais n'est-ce pas une exigence nécessaire quel que soit le rendu côté serveur ? (par exemple, il devrait rendre le même état, si je rafraîchis sur la route actuelle, même s'il s'agit d'un SPA)

Ce serait le cas, mais vous voudrez peut-être qu'un composant imbriqué gère sa propre récupération de données, pour une raison quelconque.

Par exemple, un composant qui se répète sur de nombreuses pages, mais chaque page n'a pas beaucoup de besoins en récupération de données.

@chemoish Je ne sais pas si nous sommes sur la même

Par exemple, j'ai trois composants imbriqués :

  • composant1 (dataFetch1 statique)

    • component2 (static dataFetch2)

    • composant3 (dataFetch3 statique)

Chacun d'eux a ses propres méthodes "componentDidMount", avec ses propres déclarations dataFetching (dispatching des actions via sa méthode dataFetching statique).

Si je n'ai pas de rendu côté serveur et que je rafraîchis l'URL actuelle, mes composants seront montés et déclencheront toutes les actions nécessaires pour charger toutes les données requises par la suite.

Avec le rendu côté serveur, votre fonction match et renderProps extraira les trois composants, afin que je puisse accéder à toutes les méthodes statiques de dataFetching, ce qui me permettra ensuite de récupérer toutes les données nécessaires pour le rendu initial côté serveur ?

Avez-vous une référence à votre match function partir de votre exemple fourni ? THX.

@ms88privat renderProps.components est un ensemble de composants de routeur, il ne va pas plus loin que cela. @chemoish signifiait qu'avec son implémentation, vous ne pouvez pas décrire les besoins de récupération de données sur des composants plus profonds.

@DominicTobias merci , avez-vous une solution à ce problème ? Existe-t-il une possibilité d'obtenir tous les composants imbriqués ?

Cela peut probablement aider? https://github.com/gaearon/react-side-effect
Utilisé pour collecter toutes les balises méta des éléments imbriqués : https://github.com/nfl/react-helmet

Désolé d'avoir à nouveau bousculé cette discussion, mais j'ai récemment rencontré le même problème de pré-remplissage de l'état avec une action asynchrone.

Je peux voir que @erikras a déplacé son projet passe- redux-async-connect . Je me demande si quelqu'un a trouvé une autre solution ?

@vtambourine J'ai regardé https://github.com/markdalgleish/redial ce qui est très utile

Oui, je l'ai parcouru. Mais je n'ai pas compris comment m'assurer que les données
la récupération accrochée ne s'exécutera pas une deuxième fois après la réinitialisation du code à n
client.
Le Пт, 18 арта 2016 г. à 22h54, Sean Matheson [email protected]
a écrit:

@vtambourine https://github.com/vtambourine J'ai regardé
https://github.com/markdalgleish/redial ce qui est très utile

-
Vous recevez ceci parce que vous avez été mentionné.
Répondez directement à cet e-mail ou consultez-le sur GitHub
https://github.com/reactjs/redux/issues/99#issuecomment-198517067

Aussi curieux de savoir si quelqu'un a trouvé une solution stable pour ce défi. J'adore le passe- partout de @vtambourine l'a mentionné, il est passé à redux-async-connect, ce qui semble ne pas être une solution stable à long terme : #81 Redux-async-connect est-il mort ? .

@vtambourine, il existe une fourchette disponible sur https://github.com/makeomatic/redux-connect et bien entretenue. Il a une API similaire avec quelques modifications, consultez-le si vous êtes intéressé

pour ceux qui s'intéressent à une solution de redux avec middleware comme mentionné par @gaearon, j'ai un exemple de projet qui implémente cette technique et permet aux composants eux-mêmes de demander les données dont ils ont besoin côté serveur

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

Comment tester le créateur d'action unitaire avec cette approche ?

Cette page vous a été utile?
0 / 5 - 0 notes