Redux: ¿La mejor técnica de carga asíncrona del lado del servidor?

Creado en 15 jun. 2015  ·  63Comentarios  ·  Fuente: reduxjs/redux

En primer lugar, me encanta esta biblioteca y los patrones que están usando. 👍👍

Estoy tratando de usar redux para crear una aplicación isomórfica. Está funcionando muy bien hasta ahora, excepto que necesito averiguar cómo esperar hasta que mis tiendas se hayan cargado (en el servidor) antes de devolver la carga de la página inicial. Idealmente, la carga debería tener lugar en las tiendas mismas, pero cuando llamo a dispatch(userActions.load()) , la tienda tiene que devolver el nuevo estado (es decir, return { ...state, loading: true }; ), por lo que no puede devolver una promesa a esperar. dispatch() devuelve la acción que se le ha pasado por alguna razón. Realmente me gustaría algo como ...

dispatch(someAsyncAction, successAction, failureAction) => Promise

... donde la promesa no se resuelve hasta que se envía una de las otras dos acciones.

¿Es ese el tipo de cosas que podrían habilitarse con el patrón de middleware?

¿Estoy totalmente fuera de lugar y ya hay una forma sencilla de hacer esto?

Gracias.

Comentario más útil

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

Todos 63 comentarios

¡Hey gracias!

Lo ideal es que la carga se realice en las propias tiendas.

Redux obliga a que las tiendas sean completamente sincrónicas. En su lugar, lo que describas debería suceder en el creador de acciones.

Creo que incluso puede ser posible con el middleware de procesador predeterminado. Tu creador de acciones se vería así:

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

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

y manejar las acciones reales (granulares) en la Tienda.

También es posible escribir un middleware personalizado para eliminar el texto estándar.

¡Genio! Pensé que estaba pasando por alto algo obvio. Me gusta esa separación de _hacer_ y _storing_.

Espero ver crecer esta biblioteca, aunque ya está bastante completa. Saludos, @gaearon!

También puede escribir un middleware personalizado como este

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

y utilícelo en lugar del predeterminado.

Esto te permitirá escribir creadores de acciones asíncronas como

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

y hacer que se conviertan en

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

Oh, eso también es bueno, y más de lo que tenía en mente cuando hice la pregunta original. No puedo decir si me gusta la compensación de reducir el número de constantes de acción a cambio de agregar if s para verificar el readyState dentro de la tienda. Creo que preferiría tener versiones adicionales _SUCCESS y _FAILURE de cada acción solo para evitar poner un if dentro de un case .

Pero gracias.

Sí, eso depende totalmente de tu gusto. Podría tener una versión similar que convierta types: { request: ..., success: ..., failure: ... } en acciones. Este es el punto de convertirlo en un middleware en lugar de hornear en la biblioteca: todos tienen su propio gusto para estas cosas.

// 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 hombre, me encanta esa solución. Mucho mejor que tener then() y llamadas adicionales a dispatch() como la primera solución que propuso. ¡Hurra por el middleware!

¡Déjame saber cómo (y si ;-) funciona!
Realmente no hemos probado mucho el middleware personalizado.

Dejaste un } (eso es -1 punto 😀), ¡pero funcionó a las mil maravillas! Primera vez.

: +1:

@erikras Tengo curiosidad por

Esto es solo un pseudocódigo, así que no pegue esto en ningún lado, pero estoy usando react-router (cuya api está cambiando tan rápido como la de redux) algo como esto:

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

¿Eso aclara algo?

@iest

Citando su problema en Slack:

Tengo un controlador de ruta con

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

donde UserActions.fooBar() es:

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

luego en el render del servidor estoy cediendo:

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

pero no funciona.

Creo que el problema aquí es que en realidad no devuelve nada del método anidado de fooBar .

Quite los tirantes:

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

o agregue una declaración return explícita:

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

De cualquier manera, podría ser más fácil usar un middleware de promesa personalizado como se sugirió anteriormente.

@erikras Con respecto a su último comentario en el que está llamando al método fetchData sobre lo que tiene como initialState.components (en la devolución de llamada de Router.run), el objeto del que obtiene las referencias de componentes solo devuelve los manejadores de ruta coincidentes. ¿Qué piensa sobre cómo llegar a componentes que podrían no ser un controlador de ruta coincidente, es decir, un componente secundario, pero que necesitan obtener datos?

aquí hay un ejemplo de lo que estoy hablando

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

producción:

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

No tendrás acceso a Bar en Routes

@erikras ¡Fantástico! Ese es exactamente el tipo de ruta que quiero seguir. Gracias por compartir.

@iest Espero que el juego de palabras fuera intencional, "seguir una ruta" iterando a través de las rutas coincidentes. :-)

@mattybow Eso es cierto. Si _realmente_ necesita un componente que no está en sus rutas para cargar algo, entonces la única opción es ejecutar React.renderToString() una vez (descartando el resultado), hacer toda la carga en componentWillMount() , y de alguna manera guarda las promesas sobre la marcha. Esto es lo que estaba haciendo con mi propia solución de enrutamiento de cosecha propia antes de la renderización del lado del servidor compatible con react-router . Sugeriría que la necesidad de componentes que no sean de ruta para realizar la carga podría ser un síntoma de un problema de diseño. En la mayoría de los casos de uso, una ruta sabe qué datos necesitarán sus componentes.

@erikras
¿Tiene algún repositorio público para ver un ejemplo completo de su solución allí?

@transedward Desearía

+1 en el ejemplo isomorfo avanzado
¡Me encanta a dónde va esto!

@transedward Aquí hay un proyecto de muestra con toda la tecnología de vanguardia que he improvisado. https://github.com/erikras/react-redux-universal-hot-example/

@erikras ¡ Esto es increíble! ¿Puede enviar un PR para agregarlo a esta sección de "kits de inicio" de los documentos README y React Hot Loader?

¡Gracias! RP enviados.

@erikras Genial - ¡Gracias!

Solo una nota que, basado en algunos de los ides en esta conversación, hice un middleware para manejar promesas: https://github.com/pburtchaell/redux-promise-middleware.

@pburtchaell También existe esta biblioteca de @acdlite . https://github.com/acdlite/redux-promise

Dos pensamientos sobre esto:

  1. Las promesas que se convierten en acciones pueden transmitirse junto con la acción y colocarse en la Tienda Redux.

Luego, para saber si su página está lista para renderizarse, simplemente verifique si se han completado todas las promesas. Quizás use un software intermedio que encadena todas las promesas pasadas y proporciona una promesa para cuando no hay promesas pendientes.

  1. ¿Qué tal si permitimos que los selectores realicen acciones por los datos faltantes?

Suponga que desea representar el mensaje 3, entonces su contenedor de mensajes generará <Message id={3}> y el selector de mensajes verificará si state.msgs[3] existe y, de no ser así, enviará una promesa de carga de mensajes.

Por lo tanto, combine los dos y los componentes seleccionarían automáticamente los datos necesarios y sabrían cuándo están listos.

Estoy bastante seguro de que “no pongas nada que no se pueda serializar en la tienda o en acciones”. Es uno de los invariantes que me ha funcionado muy bien (y me permitió viajar en el tiempo, por ejemplo) y debe haber razones _muy_ convincentes para considerar cambiarlo.

Luego, para saber si su página está lista para renderizarse, simplemente verifique si se han completado todas las promesas. Quizás use un software intermedio que encadena todas las promesas pasadas y proporciona una promesa para cuando no hay promesas pendientes.

En realidad, esto no requiere poner promesas en la tienda al final, y me gusta esto. La diferencia es que, al final de la cadena de envío, las acciones sin procesar no contienen ninguna promesa. En su lugar, son "recopilados" por dicho middleware.

Una cosa que hago a menudo cuando trabajo con promesas es mantener una referencia a una promesa para que cuando entren otras solicitudes para lo mismo, simplemente devuelva la misma promesa, proporcionando eliminación de rebotes. Luego elimino la referencia algún tiempo después de que se completó la promesa, proporcionando almacenamiento en caché configurable.

Realmente tengo que comenzar a usar Redux en aplicaciones reales, porque me pregunto qué hacer con esas referencias en Redux. Quiero colocarlos en la tienda para convertir el actionCreator en apátrida (o al menos hacer explícito el estado). Pasar la promesa a través de una acción es una buena forma de exportarla, pero luego tendría que recuperarla de alguna manera. Mmm.

Realmente estoy esperando la respuesta al último punto de @wmertens .
Evitar múltiples llamadas simultáneas (no hacer nada si hay algo pendiente) es un caso de uso frecuente, no estoy seguro de cuál es la mejor manera de responder eso (porque no puede acceder al estado de la tienda desde un actionCreator).

¿Debería el usuario de actionCreator (el componente) verificar cada vez en el estado si puede o no enviar la acción? Entonces tienes que hacerlo todo el tiempo ... ¿Quizás necesitas introducir tu propia anotación @connect que envuelva esto?

porque no puedes acceder al estado de la tienda desde un actionCreator

¿Por qué? Puede hacerlo bien si usa middleware thunk .

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

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

    dispatch(doSomething());
  };
}

store.dispatch(maybeDoSomething());

eso lo resuelve!
Pensé por un tiempo (sin ninguna razón) que acceder al creador de acciones en acción era una mala práctica ^^ ya que la persona que llama al creador de acciones puede acceder al estado, no veo por qué el creador de acciones no debería hacerlo, así que sí, genial, podemos hacer esto :)

Gracias @gaearon

@gaearon , ¿es http://gaearon.github.io/redux/docs/api/applyMiddleware.html a su respuesta anterior:

También puede escribir un middleware personalizado como este

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

y utilícelo en lugar del predeterminado.
Esto te permitirá escribir creadores de acciones asíncronas como

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

y hacer que se conviertan en

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

También,

Creo que para la última parte, quisiste decir:

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

Supongo que solo depende de si uno quiere crear acciones separadas para las devoluciones de llamada success o failure de la promesa, en lugar de usar la generada automáticamente.

En su ejemplo de procesador:

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

entonces no necesariamente tendría sentido tener una devolución de llamada de error genérico por no poder obtener una salsa secreta, ya que podría haber diferentes circunstancias para buscar la salsa.

Por lo tanto, puedo ver el modelo de procesador un poco más flexible.

¿Posiblemente algo como el registro o tal vez incluso la alternancia de un "indicador de ocupado de async en progreso" son ejemplos más apropiados de middleware?

@ justin808

Ambos están bien. Elija lo que sea menos detallado para usted y lo que funcione mejor para su proyecto. Mi sugerencia es comenzar con el uso de procesadores y, si ve que algún patrón se repite, extráigalo en un middleware personalizado. También puedes mezclarlos.

Creé un ActionStore para separar el estado de las acciones desencadenadas (carga, éxito, error) del otro estado. Pero no sé si esto va en contra de la base de Redux / Flux. He publicado en stackoverflow sobre esto.

@gabrielgiussi Creo que https://github.com/acdlite/redux-promise también puede lograr lo que desea sin que tenga que almacenar promesas en el estado. Se supone que el estado es serializable en todo momento.

@wmertens gracias por el consejo. Voy a echar un vistazo al repositorio, pero ¿por qué mi estado no sería serializable? ¿O lo dices solo para que tome nota?

@gabrielgiussi No
poniendo promesas o funciones en la tienda. En cualquier caso, ese proyecto
también debería funcionar bien, creo.

El lunes 10 de agosto de 2015 a las 19:15, gabrielgiussi [email protected] escribió:

@wmertens https://github.com/wmertens gracias por el consejo. voy a
eche un vistazo al repositorio, pero ¿por qué mi estado no sería serializable? O tu
decirlo solo para que tome nota?

-
Responda a este correo electrónico directamente o véalo en GitHub
https://github.com/gaearon/redux/issues/99#issuecomment -129531103.

Wout.
(escrito en el móvil, disculpe la concisión)

En realidad, en la tienda coloco objetos de acción personalizados, son simplemente inmutables. Registro con atributos simples (id, estado, carga útil) y un activador de acción que crea y devuelve una Promesa, por lo que no voy a poner Promesas en la Tienda. Pero probablemente estoy rompiendo algo en otro lugar, je. Gracias @wmertens.

@gabrielgiussi

y un disparador de acción que crea y devuelve una Promesa

No ponga funciones o cualquier otra cosa que no sea serializable, en el estado.

Perdón. Traté de decir

y un disparador de función que crea y devuelve una Promise

Lo que en realidad estoy poniendo en la tienda es un objeto Action (el nombre no era el mejor):

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

Creé una biblioteca para resolver este problema (vea el n. ° 539); funciona con promesas de devolución de middleware para acciones pendientes y esperando que se resuelvan todas esas promesas.

@gaearon este código que escribió https://github.com/rackt/redux/issues/99#issuecomment -112212639,

¿Es esto algo que está incluido en la biblioteca redux o es algo que tengo que crear manualmente? Lo siento si esta es una pregunta nueva, solo ingresando a React / Flux (Redux). Acabo de comenzar este tutorial https://github.com/happypoulp/redux-tutorial

@ banderson5144

No esta incluido Está ahí para darte una idea de lo que puedes hacer, pero eres libre de hacerlo de otra manera.
Algo similar se publicó como https://github.com/pburtchaell/redux-promise-middleware.

Gracias por esta útil información. Quería arrancarte los sesos al restablecer una tienda.

  • Restableciste el estado de la tienda para un nuevo usuario
  • Espera a que se completen algunas acciones asíncronas antes de darle al usuario la tienda y el html
  • Una acción asíncrona que se estaba ejecutando anteriormente para otro usuario se completa y su tienda se contamina

¿Cómo resolvieron eso? ¿Tienen alguna idea de cómo hacerlo? ¿Solo una nueva tienda para cada usuario funcionaría en su lugar?

¿Estás hablando de renderizado de servidores? Crea una nueva tienda en cada solicitud. Tenemos una guía para la representación del servidor en los documentos.

Gracias lo haré

Después de intentar comprender ...

¿Es esto demasiado ingenuo? (nadie más parece hacer esto, creo)

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

También estaba tratando de encontrar una solución para @mattybow 's pregunta https://github.com/rackt/redux/issues/99#issuecomment -112980776 (componentes anidados manejar la información de ir a buscar), pero no hubo éxito (no estaba seguro de cómo cobrar promesas de componentWillMount ).

@chemoish También estoy tratando de entender la representación del lado del servidor con react y redux. El ejemplo de la documentación no maneja muy bien este caso de uso. No quiero especificar y acoplar cada solicitud de API en el servidor con mis componentes nuevamente. El componente solo debe especificar cómo obtener los datos necesarios (que luego el servidor debe recoger).

Sus soluciones se ven bastante bien para lograrlo. ¿Te funcionó esto? Gracias

Editar: ¿Tengo razón en que "componentDidMount" no se activa de nuevo en el cliente cuando se procesa en el servidor?

@ ms88privat Todavía no he recibido muchos comentarios sobre la solución, ni he probado sus límites.

Sin embargo, la solución planteada anteriormente requiere que cada página conozca los datos de todos sus componentes secundarios. No he profundizado en los componentes anidados que se preocupan por la gestión de datos en sí mismos (debido a la recopilación de promesas anidadas).

Parece estar haciendo lo que cabría esperar, por lo que es lo suficientemente bueno para mí por ahora.


componentDidMount se activará de nuevo (consulte https://facebook.github.io/react/docs/component-specs.html#mounting-componentdidmount). Puede utilizar este u otro método de ciclo de vida que se adapte a sus necesidades.

Soluciono esto evitando que se ejecute el código fetch si la tienda ya está llena (o cualquier lógica comercial que considere adecuada).

Examine https://github.com/reactjs/redux/blob/master/examples/async/actions/index.js#L47 para tener una idea de lo que estoy hablando.

@quimica

Soluciono esto evitando que se ejecute el código de recuperación si la tienda ya está llena

Okey, lo tengo. Gracias.

Sin embargo, la solución planteada anteriormente requiere que cada página conozca los datos de todos sus componentes secundarios.

Tal vez leí mal su solución, pero ¿no es este un requisito necesario independientemente de la representación del lado del servidor? (por ejemplo, debería representar el mismo estado, si actualizo la ruta actual, incluso si es un SPA)

Lo haría, pero es posible que desee que un componente anidado administre su propia obtención de datos, por el motivo que sea.

Por ejemplo, un componente que se repite en muchas páginas, pero cada página no tiene muchas necesidades de obtención de datos.

@chemoish No estoy seguro de si estamos en la misma página. Permítanme intentar explicar cuál es mi punto de vista.

Por ejemplo, tengo tres componentes anidados:

  • componente1 (DataFetch1 estático)

    • componente2 (DataFetch2 estático)

    • componente3 (DataFetch3 estático)

Cada uno de ellos tiene sus propios métodos "componentDidMount", con sus propias declaraciones dataFetching (despachando acciones a través de su método estático dataFetching).

Si no tengo una representación del lado del servidor y actualizo la URL actual, mis componentes montarán y activarán todas las acciones necesarias para cargar todos los datos requeridos posteriormente.

Con la representación del lado del servidor, su función match y renderProps extraerán los tres componentes, por lo que puedo acceder a todos los métodos de búsqueda de datos estáticos, que luego me permitirán obtener todos los datos necesarios para la representación inicial del lado del servidor?

¿Tiene una referencia a su match function del ejemplo proporcionado? Gracias.

@ ms88privat renderProps.components es una matriz de componentes del enrutador, no va más allá de eso. @chemoish quiso decir que con su implementación no se pueden describir las necesidades de obtención de datos en componentes más profundos.

@DominicTobias thx, ¿tienes una solución a este problema? ¿Existe la posibilidad de obtener todos los componentes anidados?

¿Probablemente esto pueda ayudar? https://github.com/gaearon/react-side-effect
Se utiliza para recopilar todas las metaetiquetas de elementos anidados: https://github.com/nfl/react-helmet

Perdón por volver a abordar esta discusión, pero recientemente me encontré con el mismo problema de estado de llenado previo con acción asíncrona.

Puedo ver que @erikras movió su proyecto redux-async-connect . Me pregunto si alguien encontró otra solución.

@vtambourine He estado mirando https://github.com/markdalgleish/redial que es bastante útil

Sí, lo he revisado. Pero no entendí cómo asegurarme de que los datos
la búsqueda de hooked no se ejecutará por segunda vez después de la reinicialización del código an
cliente.
El Пт, 18 марта 2016 г. a las 22:54, Sean Matheson [email protected]
escribió:

@vtambourine https://github.com/vtambourine He estado mirando
https://github.com/markdalgleish/redial que es bastante útil

-
Recibes esto porque te mencionaron.
Responda a este correo electrónico directamente o véalo en GitHub
https://github.com/reactjs/redux/issues/99#issuecomment -198517067

También es curioso saber si alguien ha encontrado una solución estable para este desafío. Me encanta el texto estándar de @vtambourine , se ha movido a redux-async-connect, lo que parece que puede no ser una solución estable a largo plazo: # 81 ¿Redux-async-connect está muerto? .

@vtambourine hay una bifurcación que está disponible en https://github.com/makeomatic/redux-connect y está bien mantenida. Tiene una API similar con algunos cambios, compruébalo si estás interesado

para aquellos interesados ​​en una solución redux con middleware como lo menciona @gaearon , tengo un proyecto de ejemplo que implementa esta técnica y permite que los propios componentes soliciten los datos que necesitan del lado del servidor

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

¿Cómo probar un creador de acciones con este enfoque?

¿Fue útil esta página
0 / 5 - 0 calificaciones

Temas relacionados

elado picture elado  ·  3Comentarios

mickeyreiss-visor picture mickeyreiss-visor  ·  3Comentarios

timdorr picture timdorr  ·  3Comentarios

jbri7357 picture jbri7357  ·  3Comentarios

ms88privat picture ms88privat  ·  3Comentarios