【问题标题】:React + Redux: Component does not updateReact + Redux:组件不更新
【发布时间】:2016-06-30 06:00:22
【问题描述】:

尝试使用 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'));

顶级容器App:

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 在 promise 被解析时接收它,并用获取的数据更新状态。此时,我希望顶级 App 组件和 Showcase 组件检测到商店已更新并重新渲染,但我在控制台中看不到它。

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

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

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

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

更新:

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

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 的 props 已使用来自 next state 的值进行了更新。尽管如此,这并没有导致组件重新渲染:

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

【问题讨论】:

  • 您尝试将密钥添加到根减速器对象吗? const rootReducer = combineReducers({ navigation: navigationReducer, resources: resourcesReducer });

标签: javascript reactjs redux


【解决方案1】:

我很惊讶地看到 state 包含来自 rootReducer 的 reducers

这就是它的工作原理。仔细看看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);

【讨论】:

  • 谢谢。您的评论更清楚地说明了状态和减速器(以及 combineReducers)之间的关系,但即使在我更新了我的代码之后(请参阅我原来问题的更新部分),状态的更新也不会导致组件的重新渲染.我现在只订阅了我的 App 组件到商店,并将 mapStateToProps 函数更改为返回 {resources: state.resourcesReducer}。我看到状态更新并且App 的道具反映了这种变化,但这不会导致应用程序(或展示柜)重新渲染。我错过了什么?
  • 好的,现在我明白发生了什么。我的Object.assign 也错了。我正在改变状态:return Object.assign (state, {resources: action.payload.data }),而我真正需要做的是返回一个新对象(例如return Object.assign ({resources: action.payload.data }, state))。傻我。不过,这似乎是一个常见的错误;我错过了关于 SO 的一个非常相似的帖子:stackoverflow.com/questions/33140008/…
猜你喜欢
  • 2021-11-15
  • 2021-10-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-13
  • 2021-02-24
  • 2019-12-29
相关资源
最近更新 更多