Javascript 更新深层不可变状态属性时,Redux不更新组件
我的问题:为什么在我的不可变状态(Map)下更新数组中对象的属性不会导致Redux更新我的组件 我正在尝试创建一个小部件,将文件上载到我的服务器,我的初始状态(从我的UploaderEducer中,您将在下面看到)对象如下所示:Javascript 更新深层不可变状态属性时,Redux不更新组件,javascript,reactjs,redux,immutable.js,Javascript,Reactjs,Redux,Immutable.js,我的问题:为什么在我的不可变状态(Map)下更新数组中对象的属性不会导致Redux更新我的组件 我正在尝试创建一个小部件,将文件上载到我的服务器,我的初始状态(从我的UploaderEducer中,您将在下面看到)对象如下所示: let initState = Map({ files: List(), displayMode: 'grid', currentRequests: List() }); onProgress: (data) => { dispatch(fi
let initState = Map({
files: List(),
displayMode: 'grid',
currentRequests: List()
});
onProgress: (data) => {
dispatch(fileUploadProgressUpdated({
index,
progress: data.percentage
}));
}
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);
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')
);
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>
);
}
}
我有一个thunk方法,它可以在事件发生时(例如进度更新)启动上载和分派操作。例如,onProgress事件如下所示:
let initState = Map({
files: List(),
displayMode: 'grid',
currentRequests: List()
});
onProgress: (data) => {
dispatch(fileUploadProgressUpdated({
index,
progress: data.percentage
}));
}
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);
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')
);
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>
);
}
}
我正在使用redux actions
来创建和处理我的操作,因此该操作的缩减器如下所示:
let initState = Map({
files: List(),
displayMode: 'grid',
currentRequests: List()
});
onProgress: (data) => {
dispatch(fileUploadProgressUpdated({
index,
progress: data.percentage
}));
}
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);
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')
);
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>
);
}
}
而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开发工具中,它显示为预期的操作。但是,我的任何组件都不会更新!将新项目添加到文件
数组会使用添加的新文件重新呈现我的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);
UploadWidget
基本上是一个拖放div和一个打印在屏幕上的文件
数组
正如我在GitHub上的许多帖子中看到的那样,我尝试使用redux immutablejs
来提供帮助,但我不知道它是否有帮助。。。这是我的根减速机:
import { combineReducers } from 'redux-immutablejs';
import { routeReducer as router } from 'redux-simple-router';
import UploaderReducer from './modules/UploaderReducer';
export default combineReducers({
UploaderReducer,
router
});
我的应用程序入口点如下所示:
let initState = Map({
files: List(),
displayMode: 'grid',
currentRequests: List()
});
onProgress: (data) => {
dispatch(fileUploadProgressUpdated({
index,
progress: data.percentage
}));
}
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);
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')
);
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>
);
}
}
为什么会这样?我认为使用Immutable.js的整个想法是比较修改过的对象更容易,不管更新的深度如何
使用Redux似乎变得一成不变并不像看上去那么简单:
然而,使用Immutable的好处似乎值得一战,我很想知道我做错了什么
2016年4月10日更新
选择的答案告诉了我做错了什么,为了完整起见,我的updateFilePropsAtIndex
函数现在只包含以下内容:
return state.updateIn(['files', index], file =>
Object.assign({}, file, fileProps)
);
这很好用!:) 首先有两个总体思路:
- 是的,Immutable.js可能很有用,但是您可以在不使用它的情况下完成相同的数据不变处理。有许多库可以帮助使不可变数据更新更易于读取,但仍然可以在普通对象和数组上操作。我在我的Redux相关库repo页面上列出了很多
- 如果React组件看起来没有更新,几乎总是因为reducer实际上在修改数据。Redux常见问题解答对此主题有一个答案,位于
file[prop]=fileProps[prop]
行看起来非常奇怪。你到底希望那里发生什么事?我要好好看看那部分
事实上,现在我看到了。。。我几乎100%确定您正在修改数据。更新程序回调到state.updateIn(['files',index])
返回的文件对象与作为参数得到的文件对象完全相同。根据Immutable.js文档,位于:
如果updater函数返回调用时使用的相同值,则不会发生任何更改。如果提供了notSetValue,则仍然如此
所以是的。您返回的值与给定的值相同,您对该值的直接变异会显示在DevTools中,因为该对象仍然挂起,但由于您返回了相同的对象Immutable.js,因此实际上不会返回层次结构上任何修改过的对象。因此,当Redux检查顶级对象时,它不会看到任何更改,也不会通知订阅者,因此组件的MapStateTrops
永远不会运行
清理减速器并从更新程序中返回一个新对象,所有这些都应该可以正常工作
(这是一个迟来的回答,但我刚才看到了这个问题,它似乎仍然是开放的。希望你现在真的修复了它…当你说“组件没有更新”时,你的意思是它根本没有收到redux的新道具吗?MapStateTops是否未收到更新的进度?减速器如何,操作在有效负载中的进度是否正确?UploaderContainer mapStateToProps函数根本不会启动,尽管在DevTools中看到状态使用预期值更新。我有单元测试来检查我正在做的工作是否有效,所以我觉得在Redux级别有一些我不理解的东西,但我不知道它是什么!你能在JSBin上在线获得一个工作示例吗?解决这个问题会更容易。您是否在
UploaderReducer
文件中混合了不可变映射和普通对象?分派操作时是否调用了您的updateFilePropsAtIndex
函数?抱歉,我没有对此做出响应。我目前正在升级React/Redux和其他模块。这个周末我会试着去做,然后再打给你。谢谢你的详细回答!我抽出时间来实施你指给我的改变,它解决了所有问题。谢谢你的帮助!:)@ChrisPaton您能分享您的相关更改吗?@Sag1v如果您查看我问题末尾的更新,我将我的最终更改发布到updateFilePropsAtIndex。这就是我要改变的一切,而且效果很好@ChrisPatonupdatein()
immutablejs函数正确吗?要更新这种状态而不在没有它的情况下对其进行变异是否如此困难?