使用Redux + ReactRouter在React App中进行异步数据流?

f1n*_*1nn 21 reactjs react-router redux

我正在使用ReduxReact-router制作一个简单的Mail应用程序.由于我是Redux的新手,我不太了解Redux + Router中的实际数据流.

我想要的是什么

  1. 在页面启动(/)之后,MailListComponent从服务器获取消息数组.此时不显示MessageComponent,因为它没有单个消息来为其获取数据.
  2. 之后state.messages:[]被取出,应用程序被导航到的所述第一消息state.messages:[](/messages/1)`.
  3. 转换完成后,将显示MessageComponent并获取id = 1 info的消息,并且它在单独的请求中是附件.

这是组件模型:

组件模型

我在做什么

// 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;
  }
}
Run Code Online (Sandbox Code Playgroud)

由于我使用promiseMiddleware,LOAD_MESSAGES,LOAD_MESSAGES_REQUEST并且LOAD_MESSAGES_FAILURE是作为请求dispacted /messages结束.

现在:

  1. 可以loadMessages()MailListComponent的 componentDidMount中调度吗?
  2. 如何过渡到/messages/1正确的?
  3. 我应该activeMessageId<Integer>在我的州创建吗?
  4. 所有这些组件应如何与React-Router连接?

这是我目前的尝试:

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>
  );
};
Run Code Online (Sandbox Code Playgroud)

你能给我一些观点,如何连接点?什么是poper异步数据流逻辑?

我正在使用isomorphic-redux示例作为我的应用程序的基础.虽然是同构的,但正常的Redux app之间不应该有太大的区别

谢谢.

UPDATE

其中一个想法 - 设置onEnter挂钩<IndexRoute component={Content}/>,将获取消息,设置为状态和初始化转换.是redux +路由器的方式吗?

但是,这种方式也可能相当棘手,因为/messages只适用于经过身份验证的用户(其中store.getState().auth.get('loaded') == true)

Sti*_*itt 17

在我看来,服务器端渲染很重要.没有它,您将提供只在客户端生活的空白页面.它会严重影响你的SEO.因此,如果我们认为服务器端呈现很重要,我们需要一种方法来获取适合服务器端呈现的数据.

望着的文档服务器端渲染ICW反应路由器,这里是我们发现:

  • 首先我们打电话match,传递当前位置和我们的路线
  • 然后我们打电话ReactDOMServer.render,传递它renderProps我们得到的match

很明显,在进入渲染阶段之前,我们需要访问所获取的数据.

这意味着我们不能使用组件生命周期.我们也不能使用onEnter或任何其他仅在渲染已经开始时触发的钩子.在服务器端,我们需要渲染开始之前获取数据.这意味着我们需要能够确定什么,从获取renderProps我们从得到match.

常见的解决方案是fetchData在顶层组件上放置一个静态函数.在你的情况下,它可能看起来像这样:

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

我们可以fetchData在服务器端找到这个函数,并在继续渲染之前在那里调用它,因为match它给了我们renderProps包含匹配的组件类.所以我们可以循环遍历它们并获取所有fetchData函数并调用它们.像这样的东西:

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));
Run Code Online (Sandbox Code Playgroud)

fetchData返回结果store.dispatch,这将是一个Promise.在客户端,这将只显示一些loading屏幕,直到Promise完成,但在服务器端,我们将需要等到发生这种情况,所以当我们进入渲染阶段时,我们实际上在商店中有数据.我们可以用Promise.all它:

// 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();
})
Run Code Online (Sandbox Code Playgroud)

你去吧 我们将完全填充的页面发送给客户端.在那里,我们可以使用onEnter或生命周期钩子或任何其他方便的方法来获取用户导航客户端时所需的后续数据.但是我们应该尝试确保我们在组件本身上有一个函数或注释(初始操作?),这样我们就可以预先获取服务器端渲染的数据.

  • 我只想指出这是一个*mail*应用程序.这意味着搜索引擎无法访问**.所以SEO是一个有争议的问题.如果客户端无论如何都会渲染React组件......为什么要为它们提供预渲染页面呢? (2认同)

Eri*_*bar 4

我一直在开发一个相当大的应用程序(React、Redux、React Router 等),具有非常相似的功能(带有侧边栏的消息浏览器 + 搜索栏/工具等),它在结构上几乎你上面列出的内容。利用React 的组件生命周期对我们来说效果非常好。

基本上由组件来决定,“给定这些数据(消息、加载等),我应该是什么样子和/或做什么?”。

我们一开始就搞乱了 onEnter 和其他“组件外部”策略,但它们开始感觉过于复杂。同样相关的是你关于存储的问题activeMessageId。如果我正确理解您的场景,这应该是从params.id示例中您当前的路线可靠地得出的。

了解这种方法为我们完成的一些事情

在此输入图像描述

当然,这个示例被精简/简化了很多,但它总结了“请求消息”部分,并且非常接近为我们工作的实际方法。

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>
    )
  }
});
Run Code Online (Sandbox Code Playgroud)

我很想知道这是否对您有帮助或者您是否会登陆其他地方。我看到围绕这些主题出现了类似的问题,并且对您发现的内容感兴趣。