Universal React / redux不会在状态更新时呈现

时间:2016-05-05 15:47:30

标签: javascript reactjs redux flux

我使用this boilerplate并遇到问题,我的容器在状态更新后没有重新渲染。我有两个被调度的动作,1)LOAD - 在AJAX请求开始时调度和2)LOAD_SUCCESS - 一旦数据成功返回。

从reducer调用LOAD_SUCCESS操作,但之后没有任何反应。这个代码曾经在几周前工作(项目暂停了一段时间),但不再有效。有什么想法吗?

import superagent from 'superagent';
import config from '../../config';

const LOAD = 'cnnm/sectionFront/LOAD';
const LOAD_SUCCESS = 'cnnm/sectionFront/LOAD_SUCCESS';
const LOAD_FAIL = 'cnnm/sectionFront/LOAD_FAIL';

const initialState = {
  loaded: false,
  loading: false,
  currentSection: null,
  data: {}
};


export default function reducer(state = initialState, action = {}) {
  switch (action.type) {
    case LOAD:
      return {
        ...state,
        loading: true,
        loaded: false
      };
    case LOAD_SUCCESS:
      const result = {
        ...state,
        loading: false,
        loaded: true,
        error: null,
        currentSection: action.sectionID
      };
      result.data[action.sectionID] = action.data;
      return result;
    case LOAD_FAIL:
      return {
        ...state,
        loading: false,
        loaded: true,
        data: null,
        error: action.error
      };
    default:
      return state;
  }
}


export function load() {

  return (dispatch, getState) =>{

    dispatch({
      type: LOAD
    });


    const globalState = getState();
    const endpoint = config.endpoints.sectionFronts[globalState.routing.locationBeforeTransitions.pathname];

    if ( globalState.sectionFront.data[globalState.routing.locationBeforeTransitions.pathname] === undefined ){
      superagent.get( endpoint )
      .end( (err, resp) => {
        if ( err ){
          dispatch({
            type: LOAD_FAIL,
            error: err
          });
        }
        else {

          try {
            const data = JSON.parse( resp.text );
            dispatch({
              type: LOAD_SUCCESS,
              sectionID: globalState.routing.locationBeforeTransitions.pathname,
              data
            });
          }
          catch ( error ){
            console.warn('Error trying to parse section front', error);
            dispatch({
              type: LOAD_FAIL,
              error: error
            });
          }

        }

      });

    }
    else {
      // Already have the data cached
      dispatch({
        type: LOAD_SUCCESS,
        sectionID: globalState.routing.locationBeforeTransitions.pathname,
        data: globalState.sectionFront.data[globalState.routing.locationBeforeTransitions.pathname]
      });
    }
  };

}

货柜代码:

import React, {Component, PropTypes} from 'react';
import {connect} from 'react-redux';
import Helmet from 'react-helmet';
import Col from 'react-bootstrap/lib/Col';
import Row from 'react-bootstrap/lib/Row';
import { asyncConnect } from 'redux-async-connect';
import { load as loadSectionFront } from 'redux/modules/sectionFront';
import {bindActionCreators} from 'redux';
import { Loading } from 'components';


import Grid from 'react-bootstrap/lib/Grid';
import { cards as cardComponents } from 'components';

@asyncConnect([{
  deferred: true,
  promise: ({store: {dispatch, getState}}) => {
    return dispatch(loadSectionFront());
  }
}])

@connect(
  state => {
    return {
      section: state.sectionFront.data,
      currentSection: state.sectionFront.currentSection,
      loaded: state.sectionFront.loaded,
      loading: state.sectionFront.loading
    };
  },
  dispatch => bindActionCreators( {loadSectionFront}, dispatch)
)

export default class SectionFront extends Component {

  static propTypes = {
    section: PropTypes.object,
    loaded: PropTypes.bool,
    loading: PropTypes.bool,
    currentSection: PropTypes.string,
    loadSectionFront: PropTypes.func,
  }

  mapSecionNameToPrettyName = ( sectionID ) => {

    switch ( sectionID ){

      case '/':
        return 'Top News';
      case '/video':
        return 'Video';
      case '/technology':
        return 'Technology';
      case '/media':
        return 'Media';
      case '/investing':
        return 'Investing';
      case '/news/economy':
        return 'Economy';
      case '/pf':
        return 'Personal Finance';
      case '/retirement':
        return 'Retirement';
      case '/autos':
        return 'Autos';
      case '/smallbusiness':
        return 'Small Business';
      case '/news/companies':
        return 'Companies';
      case '/luxury':
        return 'Luxury';
      case '/real_estate':
        return 'Real Estate';
      default:
        return 'Not found';
    }
  }

  render() {
    const {section, loaded, currentSection, loading } = this.props;
    const sectionName = this.mapSecionNameToPrettyName( currentSection );
    const styles = require('./SectionFront.scss');

    let cardNodes = '';
    if (loaded){
      cardNodes = section[currentSection].cards.map((card, index) => {

        switch ( card.cardType ) {
          case 'summary':
              if ( card.images === undefined || card.cardStyle === 'text'){
                return <cardComponents.SummaryTextComponent card={card} key={index} />;
              }
              else {
                if ( card.cardStyle === 'default' ){
                  return <cardComponents.SummaryDefault card={card} key={index} />; 
                }
                else if ( card.cardStyle === 'jumbo' || card.cardStyle === 'jumboBlack' ){
                  return <cardComponents.SummaryJumboComponent card={card} key={index} />;
                }
                else if ( card.cardStyle === 'jumboOverlay' ){
                  return <cardComponents.SummaryJumboOverlayComponent card={card} key={index} />;
                }
              }
            break;

          case 'marketIndices':
            return <cardComponents.MarketIndicesComponent key={index} />;

          case 'ad':
            return <cardComponents.AdComponent card={card} key={index} />;

          case 'bizDev':
            return <cardComponents.BizDevComponent card={card} key={index} />;

          default:
            return ( <div key={index}> </div>);
        }

      });
    }

    return (
      <div className={styles.sectionFront}>
        { loading ? <div className={styles.overlay}><Loading /></div> : null }
        <Grid className={styles.container}>
          <Row className={styles.sectionTitle}>
            <Col className={ 'col-xs-12 ' + styles.sectionCol}>
              <h1 className="container">{sectionName}</h1>
            </Col>
          </Row>
          {cardNodes}
        </Grid>

      </div>
    );
  }
}

1 个答案:

答案 0 :(得分:0)

根据http://redux.js.org/docs/FAQ.html#react-not-rerendering的Redux常见问题解答,99.9%的时间组件未重新渲染的原因是由于reducer直接改变了状态。这一行似乎就是这样做的:result.data[action.sectionID] = action.data;。您还需要在data内返回result的克隆版本。

现在,我实际上会期望“加载”和“加载”中的更改会正确地导致组件重新渲染,即使“数据”引用保持不变,但可能还有其他我遗漏的东西这里。