在动作调度后渲染组件

时间:2016-11-21 23:43:52

标签: javascript reactjs redux

网页加载后,我在index.js内部发送了一个点击天气API的动作store.dispatch(getWeatherReports());。此操作将通过redux进程,并最终将返回的数据添加到名为weatherReports的状态的属性中。此属性是一个空数组的对象。现在,我将粘贴代码的概述..并非所有这些都可以帮助您省去线路上的麻烦,因为从API输出数据不是我的问题。

这是我的index.js

import 'babel-polyfill';
import React from 'react';
import {render} from 'react-dom';
import configureStore from './store/configureStore';
import {Provider} from 'react-redux';
import {Router, browserHistory} from 'react-router';
import {StyleRoot} from 'radium';
import routes from './routes';
import {loadBlogs} from './actions/blogActions';
import {loadUsers, getActiveUser} from './actions/userActions';
import {getWeatherReports} from './actions/weatherActions';
import '../node_modules/bootstrap/dist/css/bootstrap.min.css';
import '../node_modules/toastr/build/toastr.min.css';
import './styles/app.scss';

const store = configureStore();
store.dispatch(loadBlogs());
store.dispatch(loadUsers());
store.dispatch(getActiveUser());
store.dispatch(getWeatherReports());

render(
  <StyleRoot>
    <Provider store={store}>
      <Router history={browserHistory} routes={routes}/>
    </Provider>
  </StyleRoot>,
  document.getElementById('app')
);

相信这会通过正确的流程来返回数据。然后我创建了一个smart组件,它采用这种状态,并将其传递给一个哑组件。

class DashboardPage extends React.Component {
  constructor(props, context) {
    super(props, context);

    this.state = {
      weatherReports: []
    };

  }

  <ContentBox
    title="What The Wind Blew In"
    content={<WeatherReport reports={this.props.weatherReports} />
  />

再次,相信我有适当的mapStateToPropsmapDispatchToProps等。然后我会在dumb组件中显示数据。 WeatherReport.js

import React, {PropTypes} from 'react';
import styles from '../common/contentBoxStyles';

const WeatherReport = ({reports}) => {
    console.log(reports);
    return (
        <div style={styles.body} className="row">
            <div style={styles.weatherBoxContainer}>
                <div className="col-sm-2 col-md-offset-1" style={styles.weatherBoxContainer.weatherCard}>
                    <div style={styles.weatherBoxContainer.weatherReport}>
                        <div style={styles.weatherBoxContainer.currentTemp}>
                            HERE IS MY PROBLEM!!
                            {reports[0].main.temp}
                        </div>
                    </div>
                    CA
                </div>
                <div className="col-sm-2" style={styles.weatherBoxContainer.weatherCard}>
                    <div style={styles.weatherBoxContainer.weatherReport}>
                        Report
                    </div>
                    UT
                </div>
                <div className="col-sm-2" style={styles.weatherBoxContainer.weatherCard}>
                    <div style={styles.weatherBoxContainer.weatherReport}>
                        Report
                    </div>
                    MN
                </div>
                <div className="col-sm-2" style={styles.weatherBoxContainer.weatherCard}>
                    <div style={styles.weatherBoxContainer.weatherReport}>
                        Report
                    </div>
                    DC
                </div>
                <div className="col-sm-2" style={styles.weatherBoxContainer.weatherCard}>
                    <div style={styles.weatherBoxContainer.weatherReport}>
                        Report
                    </div>
                    NY
                </div>
            </div>
        </div>
    );
};

WeatherReport.propTypes = {
    reports: PropTypes.array
};

export default WeatherReport;

我的根本问题是在我的调度操作返回数据之前..我试图通过我的dumb组件呈现它。因此,在尝试访问数据时收到以下错误:Uncaught TypeError: Cannot read property 'main' of undefined(…)执行以下操作时:reports[0].main.temp 发生了什么事这是阻止我的应用程序向前移动,以便store.dispatch(getWeatherReports());永远不会被调用。因此,如果我从等式中删除{reports[0].main.temp},则该过程继续并且动作将被分派。然后我可以用HMR和hooray来称呼这个等式!数据在那里......

所以我的问题,并且感谢与我的关系,我是如何得到它的,以便我的哑组件等待尝试在状态上访问这些属性,直到我的初始调度动作发生之后?

3 个答案:

答案 0 :(得分:1)

在尝试访问reports[0]之前,请检查.main是否存在。如果是undefined则显示加载微调器或其他东西......

 if (!reports[0]) return <div>Loading...</div>

 return (
   // your component like normal.
 )

答案 1 :(得分:1)

如果getWeatherReports请求是由支持promises的东西而不是回调来通知你成功的响应(例如axios),那么你可以尝试使用像 redux-这样的中间件许

redux-promise 将执行的操作是拦截调度操作,并阻止其将操作转发给商店的订户(如果承诺尚未解决)。

成功回复并且承诺得到解决后,中间件会创建一个与原始操作具有相同操作类型的新操作,但其负载包含您需要的数据。

您可以按照以下方式使用它:

import { createStore, applyMiddleware } from 'redux';
import promise from 'redux-promise'; 

const store = configureStore();
store.dispatch(loadBlogs());
...
...
store.dispatch(getWeatherReports());

const storeWithMiddleWare = applyMiddleware(promise)(store);

答案 2 :(得分:1)

似乎store.dispatch(getWeatherReports())是异步API调用。因此,您需要在DOM中呈现组件之前等待响应。

解决方案:使用redux-connect

关键点:

  •   

    它允许您请求异步数据,将它们存储在redux状态和   将它们连接到您的反应组件。

  •   

    此软件包由2部分组成:一部分允许您延迟   容器呈现,直到发生一些异步操作。另一个   将您的数据存储到redux状态,然后将加载的数据连接到您的数据   容器

在履行承诺之前,您的组件不会渲染。

主要优点是:

  • 为CSR(客户端呈现)和SSR(服务器端呈现)配置简单而优雅。

  • 您可以对您自己的Reducer中的数据加载或加载成功等提取操作做出反应

  • 您可以创建自己的中间件来处理Redux Async Connect操作
  • 您可以使用简单的redux @connect
  • 连接到其他任何地方的已加载数据
  • 最后,您可以使用Redux Dev Tools

  • 调试和查看数据
  • 此外,它还与React Router集成,以防止在加载数据之前进行路由转换。

请确保您不要忘记4个配置步骤(described here):

  1. 连接您的数据,类似于react-redux @connect
  2. 以道具的形式访问数据
  3. 连接redux async reducer
  4. 使用ReduxAsyncConnect中间件
  5. 呈现Router

    因此,DashboardPage假设 getWeatherReports() 将返回承诺),我们可以做的是:

    如果你有使用装饰器的babel预设:

    import { asyncConnect } from 'redux-connect';
    
    @asyncConnect([{
      key: 'lunch',
      promise: ({ store: { dispatch }, params }) => dispatch(getWeatherReports())
    }])
    export default class DashboardPage extends React.Component {
     ...
    }
    

    否则:

    class DashboardPage extends React.Component {
     ...
    }
    
    
    // sorry for the long name ;)
    const asyncConnectedDashboardPage = asyncConnect([{
      key: 'lunch',
      promise: store.dispatch(getWeatherReports())
    }])
    
    export default asyncConnectedDashboardPage;
    

    使用multipe promises,

    @asyncConnect([{
      promise: ({ store: { dispatch, getState } }) => {
        const promises = [];
        const state = getState();
        if (/* some condition */) {
          promises.push(dispatch(loadBlogs()))
        }
    
        if (/* some other condition */) {
          promises.push(dispatch(loadUsers()))
        }
    
        promises.push(dispatch(getWeatherReports()))
        return Promise.all(promises);
      },
    }])