React + Redux:组件不更新

React + Redux: Component does not update

尝试 React + Redux,并且可能正在做一些明显愚蠢的事情,因为触发操作以通过网络获取数据的组件在获取数据时不会更新(重新呈现)。

以下是我的代码的相关部分:

作为应用入口点的顶级 index.js:

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import { Router, browserHistory } from 'react-router';
import reduxPromise from 'redux-promise';
import createLogger from 'redux-logger';

const logger = createLogger();

import routes from './routes';
import reducers from './reducers';

const createStoreWithMiddleware = applyMiddleware(reduxPromise, logger)(createStore);

ReactDOM.render(
  <Provider store={createStoreWithMiddleware(reducers)}>
    <Router history={browserHistory} routes={routes} />
  </Provider>
  , document.querySelector('.container'));

顶级容器应用程序:

import React, {Component} from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import * as Actions from '../actions';
import Header from '../components/header';
import Showcase from '../components/showcase';

function mapStateToProps(state) {
  return {
    resources: state.resources
  }
}

function mapDispatchToProps(dispatch) {
  return {
    fetchResources: () => {
      dispatch(Actions.fetchResources());
    }
  }
}


class App extends Component {

  render() {
    console.log('props in App', this.props);
    return (
      <div>
        <Header/>
        <Showcase
          fetchResources={this.props.fetchResources}
          resources={this.props.resources}
        />
      </div>
    );
  }
}

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(App)

组件将要挂载并显示获取的数据时触发发送数据请求的操作:

import React, {Component} from 'react';
import {connect} from 'react-redux';

class Showcase extends Component {
  constructor(props) {
    super(props);
  }

  componentWillMount() {
    this.props.fetchResources();
  }

  render() {
    console.log('resources', this.props);
    return (
      <div>
        This is showcase
      </div>
    );
  }
}

export default connect(state => ({resources: state.resources}))(Showcase)

动作创作者:

import * as types from '../constants/ActionTypes';
import axios from 'axios';

export function fetchResources() {
  return {
    type: types.FETCH_FIRST,
    payload: axios.get('/sampledata/1.json')
  }
}

获取操作的减速器:

import * as types from '../constants/ActionTypes';

export default function resourcesReducer (state={}, action) {
  switch (action.type) {
    case types.FETCH_FIRST:
      console.log('about to return', Object.assign (state, {resources: action.payload.data }))
      return Object.assign (state, {resources: action.payload.data });
    default:
      return state
  }
};

最后是根减速器:

import { combineReducers } from 'redux';
import navigationReducer from './navigation-reducer';
import resourcesReducer from './resources-reducer';

const rootReducer = combineReducers({
  navigationReducer,
  resourcesReducer
});

export default rootReducer;

所以,这就是我观察到的。成功触发请求数据的操作,发送请求,reducer 在解决承诺时接收它,并使用获取的数据更新状态。此时,我希望顶级 App 组件和 Showcase 组件检测到商店已更新,并重新呈现,但我没有在控制台中看到它。

此外,我对 redux-logger 的控制台输出感到困惑:

具体来说,我很惊讶地看到 state 包含来自 rootReducer 的缩减器 — 我不知道它是否正确(Redux logger Github page 上的示例显示了没有缩减器的状态)。 redux-logger 报告的 prev state 包含与 next state 相同的 resourcesReducer 对象,这似乎也令人惊讶,尽管直觉上我希望 prev stateprev state 更多或少空。

能否请您指出我做错了什么以及如何让 React 组件响应 state 更改?

============================================= =====

更新:

1) 更改了 App 组件中的 mapStateToProps 函数,使其正确映射到减速器状态:

function mapStateToProps(state) {
  return {
    resources: state.resourcesReducer
  }
}

2) 仍然将 resources 传递给 `Showcase 组件:

  render() {
    console.log('props in App', this.props);
    return (
      <div>
        <Header navigateActions={this.props.navigateActions}/>
        React simple starter
        <Showcase
          fetchResources={this.props.fetchResources}
          resources={this.props.resources}
        />
      </div>
    );

3) 尝试通过将 resources 字符串化以查看此对象内部的实际内容来在屏幕上显示 resources

  render() {
    console.log('resources', this.props);
    return (
      <div>
        This is showcase {JSON.stringify(this.props.resources)}
      </div>
    );
  }

在屏幕上看到这个:This is showcase {}。该组件似乎没有重新渲染。

这是控制台的屏幕截图,显示 App 的道具已更新为 next state 中的值。尽管如此,这并没有导致组件重新渲染:

再次更新: 我的 javascript-fu 也很差。我并没有完全意识到通过返回 Object.assign (state, {resources: action.payload.data }); 我实际上是在改变状态,并且一个简单的参数反转可以让我实现我的意图。感谢 对 SO 的启发。

I am surprized to see that the state contains reducers from the rootReducer

这是它的工作原理。仔细看看 combineReducers().

const rootReducer = combineReducers({
  navigationReducer,
  resourcesReducer
});

认识到它不是参数列表;它是一个单一的对象参数。也许在冗长的语法中更清楚:

var rootReducer = combineReducers({
  navigationReducer: navigationReducer,
  resourcesReducer: resourcesReducer
});

resourcesReducer键指向resourcesReducer()函数返回的状态。也就是说,resourcesReducer()中的state变量只是整个状态的一部分。

传递给connect()的函数将整个状态作为参数。你的实际应该是这样的:

export default connect(state => ({
  resources: state.resourcesReducer.resources
}))(Showcase);