【发布时间】:2016-06-11 03:47:43
【问题描述】:
我的问题:为什么在我的不可变状态(映射)中更新数组中对象的属性不会导致 Redux 更新我的组件?
我正在尝试创建一个将文件上传到我的服务器的小部件,我的初始状态(来自您将在下面看到的 UploaderReducer 内部)对象如下所示:
let initState = Map({
files: List(),
displayMode: 'grid',
currentRequests: List()
});
我有一个 thunk 方法,可以在事件发生(例如进度更新)时开始上传和调度操作。例如,onProgress 事件如下所示:
onProgress: (data) => {
dispatch(fileUploadProgressUpdated({
index,
progress: data.percentage
}));
}
我使用redux-actions 来创建和处理我的动作,所以我的那个动作的reducer 看起来像这样:
export default UploaderReducer = handleActions({
// Other actions...
FILE_UPLOAD_PROGRESS_UPDATED: (state, { payload }) => (
updateFilePropsAtIndex(
state,
payload.index,
{
status: FILE_UPLOAD_PROGRESS_UPDATED,
progress: payload.progress
}
)
)
}, initState);
而updateFilePropsAtIndex 看起来像:
export function updateFilePropsAtIndex (state, index, fileProps) {
return state.updateIn(['files', index], file => {
try {
for (let prop in fileProps) {
if (fileProps.hasOwnProperty(prop)) {
if (Map.isMap(file)) {
file = file.set(prop, fileProps[prop]);
} else {
file[prop] = fileProps[prop];
}
}
}
} catch (e) {
console.error(e);
return file;
}
return file;
});
}
到目前为止,这一切似乎工作正常!在 Redux DevTools 中,它按预期显示为操作。但是,我的组件都没有更新!将新项目添加到 files 数组会使用添加的新文件重新呈现我的 UI,所以 Redux 对我这样做当然没有问题...
我使用connect 连接到商店的顶级组件如下所示:
const mapStateToProps = function (state) {
let uploadReducer = state.get('UploaderReducer');
let props = {
files: uploadReducer.get('files'),
displayMode: uploadReducer.get('displayMode'),
uploadsInProgress: uploadReducer.get('currentRequests').size > 0
};
return props;
};
class UploaderContainer extends Component {
constructor (props, context) {
super(props, context);
// Constructor things!
}
// Some events n stuff...
render(){
return (
<div>
<UploadWidget
//other props
files={this.props.files} />
</div>
);
}
}
export default connect(mapStateToProps, uploadActions)(UploaderContainer);
uploadActions 是一个使用redux-actions 创建动作的对象。
files 数组中的file 对象基本上是这样的:
{
name: '',
progress: 0,
status
}
UploadWidget 基本上是一个拖放 div 和一个打印在屏幕上的 files 数组。
我尝试使用 redux-immutablejs 提供帮助,正如我在 GitHub 上的许多帖子中看到的那样,但我不知道它是否有帮助...这是我的根减速器:
import { combineReducers } from 'redux-immutablejs';
import { routeReducer as router } from 'redux-simple-router';
import UploaderReducer from './modules/UploaderReducer';
export default combineReducers({
UploaderReducer,
router
});
我的应用入口点如下所示:
const store = configureStore(Map({}));
syncReduxAndRouter(history, store, (state) => {
return state.get('router');
});
// Render the React application to the DOM
ReactDOM.render(
<Root history={history} routes={routes} store={store}/>,
document.getElementById('root')
);
最后,我的<Root/> 组件如下所示:
import React, { PropTypes } from 'react';
import { Provider } from 'react-redux';
import { Router } from 'react-router';
export default class Root extends React.Component {
static propTypes = {
history: PropTypes.object.isRequired,
routes: PropTypes.element.isRequired,
store: PropTypes.object.isRequired
};
get content () {
return (
<Router history={this.props.history}>
{this.props.routes}
</Router>
);
}
//Prep devTools, etc...
render () {
return (
<Provider store={this.props.store}>
<div style={{ height: '100%' }}>
{this.content}
{this.devTools}
</div>
</Provider>
);
}
}
因此,最终,如果我尝试更新以下状态对象中的“进度”,React/Redux 不会更新我的组件:
{
UploaderReducer: {
files: [{progress: 0}]
}
}
这是为什么?我认为使用 Immutable.js 的整个想法是更容易比较修改后的对象,无论您更新它们的深度如何?
似乎通常让 Immutable 与 Redux 一起工作并不像看起来那么简单: How to use Immutable.js with redux? https://github.com/reactjs/redux/issues/548
但是,使用 Immutable 的吹捧好处似乎值得这场战斗,我很想弄清楚我做错了什么!
2016 年 4 月 10 日更新
选定的答案告诉我我做错了什么,为了完整起见,我的 updateFilePropsAtIndex 函数现在只包含以下内容:
return state.updateIn(['files', index], file =>
Object.assign({}, file, fileProps)
);
这很好用! :)
【问题讨论】:
-
当你说“组件没有被更新”时,你的意思是它根本没有从 redux 接收新的 props 吗? mapStateToProps 是否没有收到更新的进度?那么reducer呢,action在payload中的进度是否正确?
-
UploaderContainer mapStateToProps 函数根本不会触发,尽管在 DevTools 中看到状态已更新为预期值。我有单元测试检查我正在做的事情是否应该工作,所以我觉得在 Redux 级别有一些我不理解的东西,但我不知道它是什么!
-
你能在 JSBin 上获得一个在线工作示例吗?解决这个问题会更容易。
-
您是否在
UploaderReducerfilesList中混合了不可变映射和普通对象?调度操作时是否调用了updateFilePropsAtIndex函数?
标签: javascript reactjs redux immutable.js