React + Redux:组件不更新 [英] React + Redux: Component does not update

查看:118
本文介绍了React + Redux:组件不更新的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



以下是我的代码的相关位:



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

  import来自'react'的React; 
从'react-dom'导入ReactDOM;来自'react-redux'的
import {Provider};
从'redux'导入{createStore,applyMiddleware};来自'react-router'的
import {Router,browserHistory};
从'redux-promise'导入reduxPromise;
从'redux-logger'导入createLogger;

const logger = createLogger();

从'./routes'导入路线;
从'./reducers'导入减速器;

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

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

顶级容器应用:

 从'react'导入React,{Component};来自'redux'的
import {bindActionCreators};来自'react-redux'的
import {connect};
import * as Actions from'../actions';
从'../components/header'导入标题;
import来自'../components/showcase'的展示;

函数mapStateToProps(state){
return {
resources:state.resources
}
}

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


类App扩展组件{

render(){
console。 log('app'中的道具,this.props);
return(
< div>
< Header />
< Showcase
fetchResources = {this.props.fetchResources}
resources = {this.props.resources}
/>
< / div>
);
}
}

导出默认连接(
mapStateToProps,
mapDispatchToProps
)(App)

触发一个动作以在数据即将挂载并且应该显示所获取的数据时发送数据请求的组件:

 从'react'导入React,{Component};来自'react-redux'的
import {connect};

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

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

render(){
console.log('resources',this.props);
返回(
< div>
这是展示
< / div>
);
}
}

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

行动创造者:

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

导出函数fetchResources(){
return {
类型:types.FETCH_FIRST,
payload:axios.get('/ sampledata / 1.json')
}
}

获取操作的Reducer:

  import *作为来自'../constants/ActionTypes'的类型; 

导出默认函数resourcesReducer(state = {},action){
switch(action.type){
case types.FETCH_FIRST:
console.log('即将返回',Object.assign(state,{resources:action.payload.data}))
return Object.assign(state,{resources:action.payload.data});
默认值:
返回状态
}
};

最后是根减速器:

 从'redux'导入{combineReducers}; 
从'./navigation-reducer'导入navigationReducer;
从'./resources-reducer'导入resourcesReducer;

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

export default rootReducer;

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



另外,我对 redux-logger 的控制台输出:





具体来说,我很惊讶地看到状态包含来自rootReducer的reducers - 我不知道它是否正确(



再次更新:我的javascript-fu也很差。我没有意识到通过返回 Object.assign(state,{resources:action.payload.data}); 我实际上是在改变状态,这很简单反驳论证会让我实现我的意图。感谢关于SO的讨论以获得启发。

解决方案


我很惊讶看到状态包含来自rootReducer的缩减器


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

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

认识到它不是参数列表;它是一个单一的对象参数。也许ES5语法更清晰:

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

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



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

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


Trying out React + Redux, and probably am doing something obviously stupid, because a component that fires an action to fetch data over the network does not get updated (re-rendered) when the data is fetched.

Here are the relevant bits of my code:

The top-level index.js serving as an entry point for the app:

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'));

Top-level 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)

Component that triggers an action to sends a request for data when it is about to mount and is supposed to show the fetched data:

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)

Action Creator:

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

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

Reducer for the fetch action:

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
  }
};

and finally the root reducer:

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

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

export default rootReducer;

So, here is what I am observing. The action to request data is successfully triggered, a request is sent, the reducer receives it when the promise is resolved, and updates the state with the fetched data. At this point, I would expect the top-level App component and the Showcase component to detect that the store has updated, and to re-render, but I do not see it in the console.

Also, I am confused by redux-logger’s console output:

Specifically, I am surprized to see that the state contains reducers from the rootReducer — I don't know if it's right (an example on Redux logger Github page shows a state without reducers). It also seems surprising that the prev state as reported by redux-logger contains the same resourcesReducer object as the next state, although intuitively I would expect prev state to be more or less empty.

Could you please point out what I am doing wrong and how to get React components respond to the state changes?

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

UPDATED:

1) Changed the mapStateToProps function in the App component so that it correctly maps to reducer states:

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

2) Still passing the resources down to the `Showcase component:

  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) Trying to display resources on the screen by stringifying it to see what’s actually inside this object:

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

See this on the screen: This is showcase {}. The component does not seem to re-render.

Here’s the screenshot of the console showing that App’s props have updated with the values from the next state. Still, that did not cause the component to re-render:

UPDATED AGAIN: And my javascript-fu was poor, too. I did not quite realize that by returning Object.assign (state, {resources: action.payload.data }); I was in fact mutating the state, and that a simple inversion of arguments would let me achieve what I intended. Thanks to this discussion on SO for enlightenment.

解决方案

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

This is how it works. Take a closer look at combineReducers().

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

Recognise that it's not a list of parameters; it's a single object parameter. Perhaps it is clearer in ES5 syntax:

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

The resourcesReducer key points to the state returned by the resourcesReducer() function. That is, the state variable within the resourcesReducer() is just one part of the entire state.

The functions passed to connect() take the entire state as an argument. What yours should actually look like is this:

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

这篇关于React + Redux:组件不更新的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆