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 state
比 prev 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);
尝试 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 state
比 prev 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 });
我实际上是在改变状态,并且一个简单的参数反转可以让我实现我的意图。感谢
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);