uso usar react para método estado componentes reactjs react-router redux

reactjs - usar - redux-thunk



Flujo de datos asíncronos en la aplicación React con Redux+ReactRouter? (3)

Estoy usando Redux y React-router para hacer una aplicación de correo simple. Como soy bastante nuevo en Redux, no entiendo bien el flujo de datos real en Redux + Router.

Lo que estoy tratando de conseguir

  1. Después de que se inicie la página ( / ), MailListComponent recupera la matriz de mensajes del servidor. En este momento, MessageComponent no se muestra, ya que no tiene un solo mensaje para recuperar datos.
  2. Después de recuperar los mensajes de state.messages:[] , la aplicación se desplaza al primer mensaje de mensajes de state.messages:[] ( /messages/1 ) `.
  3. Una vez que finaliza la transición, se muestra MessageComponent y recupera el mensaje con id = 1 info y se encuentra en una solicitud separada en sus archivos adjuntos.

Aquí está el modelo de componente:

Que estoy haciendo

// MailListActions.js export function loadMessages() { return { type: ''LOAD_MESSAGES'', promise: client => client.get(''/messages'') }; } // MailListReducer.js import Immutable from ''immutable''; const defaultState = { messages: [], fetchingMessages: false }; export default function mailListReducer(state = defaultState, action = {}) { switch (action.type) { case ''LOAD_MESSAGES_REQUEST'': return state.merge({fetchingMessages: true}); case ''LOAD_MESSAGES'': return state.merge({fetchingMessages: false, messages: action.res.data || null}); case ''LOAD_MESSAGES_FAILURE'': // also do something default: return state; } }

A medida que promiseMiddleware , LOAD_MESSAGES , LOAD_MESSAGES_REQUEST y LOAD_MESSAGES_FAILURE se eliminan cuando finaliza la solicitud /messages .

Y ahora:

  1. ¿Es correcto enviar loadMessages() en componentDidMount of MailListComponent ?
  2. ¿Cómo se debe hacer la transición a /messages/1 correctamente?
  3. ¿Debo crear activeMessageId<Integer> en mi estado?
  4. ¿Cómo se deben conectar todos estos componentes con React-Router?

Aquí están mis intentos actuales:

export default (store) => { const loadAuth = (nextState, replaceState, next) => { ... }; return ( <Route name="app" component={App} path="/" onEnter={loadAuth}> <IndexRoute component={Content}/> // <== THIS IS A DUMMY COMPONENT. It diplays pre-loader until the app is transitioned to real first message <Route path="messages/:id" component={Message}/> </Route> ); };

¿Podría proporcionarme algunos puntos, cómo conectar los puntos? ¿Qué es la lógica de flujo de datos asincrónica poper?

Estoy usando el ejemplo de isomorphic-redux como base para mi aplicación. Aunque es isomorfo, no debería ser una diferencia demasiado grande entre la aplicación normal de Redux

Gracias.

ACTUALIZAR

Una de las ideas: establecer onEnter hook para <IndexRoute component={Content}/> , que recuperará los mensajes, los establecerá en el estado y la transición inicial. ¿Es la forma de enrutador redux +?

Sin embargo, esta forma también puede ser bastante complicada, porque causa /messages solo funcionan para usuarios autenticados (donde store.getState().auth.get(''loaded'') == true )


En mi humilde opinión, la representación del lado del servidor es importante. Sin él, estará sirviendo páginas vacías que solo cobran vida en el lado del cliente. Impactará gravemente su SEO. Por lo tanto, si creemos que la representación del lado del servidor es importante, necesitamos una manera de obtener datos que se ajusten a la representación del lado del servidor.

En cuanto a los documentos para la representación del lado del servidor icw react-router , esto es lo que encontramos:

  • Primero llamamos match , pasándole la ubicación actual y nuestras rutas.
  • Luego llamamos a ReactDOMServer.render , pasándole los renderProps que obtuvimos de la match

Está claro que necesitamos tener acceso a los datos obtenidos antes de pasar a la fase de renderizado.

Esto significa que no podemos utilizar el ciclo de vida de los componentes . Tampoco podemos usar onEnter o cualquier otro enganche que solo se dispare cuando el renderizado ya haya comenzado. En el lado del servidor, necesitamos recuperar los datos antes de que comience el procesamiento. Lo que significa que debemos poder determinar qué obtener de los renderProps que obtenemos de la match .

La solución común es poner una función estática fetchData en el componente de nivel superior. En tu caso podría verse algo como esto:

export default class MailListComponent extends React.Component { static fetchData = (store, props) => { return store.dispatch(loadMessages()); }; // .... }

Podemos encontrar esta función fetchData en el lado del servidor e invocarla allí antes de continuar con el procesamiento, porque la match nos proporciona renderProps que contienen las clases de componentes coincidentes. Así que solo podemos pasar por encima de ellos y tomar todas fetchData funciones de fetchData y llamarlas. Algo como esto:

var fetchingComponents = renderProps.components // if you use react-redux, your components will be wrapped, unwrap them .map(component => component.WrappedComponent ? component.WrappedComponent : component) // now grab the fetchData functions from all (unwrapped) components that have it .filter(component => component.fetchData); // Call the fetchData functions and collect the promises they return var fetchPromises = fetchingComponents.map(component => component.fetchData(store, renderProps));

fetchData devuelve el resultado de store.dispatch , que será una Promise . En el lado del cliente, esto solo mostrará una pantalla de loading hasta que se cumpla la Promesa, pero en el lado del servidor tendremos que esperar hasta que eso suceda para que tengamos los datos en la tienda cuando pasemos a la fase de procesamiento. Podemos usar Promise.all para eso:

// From the components from the matched route, get the fetchData functions Promise.all(fetchPromises) // Promise.all combines all the promises into one .then(() => { // now fetchData() has been run on every component in the route, and the // promises resolved, so we know the redux state is populated res.status(200); res.send(''<!DOCTYPE html>/n'' + ReactDOM.renderToString( <Html lang="en-US" store={app.store} {...renderProps} script="/assets/bridalapp-ui.js" /> ) ); res.end(); })

Hay que ir Enviamos una página completa al cliente. Allí, podemos utilizar onEnter o ganchos de ciclo de vida o cualquier otro método conveniente para obtener los datos subsiguientes necesarios cuando el usuario navega por el lado del cliente. Pero debemos tratar de asegurarnos de que tenemos una función o anotación (¿acción inicial?) Disponible en el propio componente para que podamos obtener datos de antemano para el procesamiento del lado del servidor.


He estado trabajando en una aplicación bastante grande (React, Redux, React Router, etc ...) con una función muy similar (navegador de mensajes con barra lateral + barra de búsqueda / herramientas, etc ...) Es casi idénticamente estructuralmente Lo que has expuesto arriba. El uso del ciclo de vida de los componentes de React nos ha funcionado muy bien.

Básicamente, dejarlo en el componente para decidir, "Dados estos datos (mensajes, carga, etc ...), ¿qué aspecto debo tener y / o hacer?".

Comenzamos jugando con OnEnter y otras estrategias "fuera del componente", pero comenzaron a sentirse demasiado complejas. También está relacionada su pregunta sobre el almacenamiento de activeMessageId . Si entiendo su escenario correctamente, esto debería derivarse de manera confiable de su ruta actual params.id en el ejemplo.

Para dar una idea de algunas cosas que este enfoque está logrando para nosotros.

Por supuesto, este ejemplo se ha simplificado / simplificado bastante, pero resume la parte de "mensajes de solicitud" y está muy cerca del enfoque real que está funcionando para nosotros.

const MailApp = React.createClass({ componentWillMount() { this._requestIfNeeded(this.props); }, componentWillUpdate(newProps) { this._requestIfNeeded(newProps); }, _requestIfNeeded(props) { const { // Currently loaded messages messages, // From our route "messages/:id" (ReactRouter) params: {id}, // These are our action creators passed down from `connect` requestMessage, requestMessages, // Are we "loading"? loading, } = props; if (!id) { // "messages/" if (messages.length === 0 && !loading) return requestMessages(); } else { // "messages/:id" const haveMessage = _.find(messages, {id}); if (!haveMessage && !loading) return requestMessage(id); } }, render() { const { messages, params: {id}, } = props; return ( <div> <NavBar /> <Message message={_.find(messages, {id})}/> <MailList message={messages} /> </div> ) } });

Me encantaría saber si esto te ayuda o si aterrizas en otro lugar. He visto preguntas similares surgiendo en torno a estos temas y me interesaría saber qué es lo que descubres.


La API de React-Router 2 abre interesantes posibilidades de obtención de datos, permitiéndole tener una capa entre el enrutador y su aplicación. Es un gran lugar para un complemento adicional como https://github.com/rackt/async-props o https://github.com/raisemarketplace/ground-control .

El gancho fetchData universal en GroundControl (específico para Redux) le da bastante poder. Te permite bloquear el componente del lado del cliente; manejar render async; y luego despachar a su reductor.

const fetchData = (done, { dispatch, clientRender }) => { // show blocking loading component setTimeout(() => { clientRender(); // non-blocking loader (props.loading) setTimeout(() => { dispatch(actions.load({ mydata }); done(); }, 1000); }, 1000) };

Los reductores se declaran en las rutas y se fetchData función fetchData para cada ruta anidada. Por lo tanto, puede tener el control de ruta de diseño padre auth. Luego, fetchData en rutas anidadas maneja lo que les importa.