2016-02-27 108 views
6

我的问题:为什么不更新我的不可变状态(Map)中的对象的属性不会导致Redux更新我的组件?Redux在深度不可变状态属性更新时没有更新组件

我试图创建一个窗口小部件,文件上传到我的服务器,我的初始状态(从我UploaderReducer里,你会看到如下图)物体看起来是这样的:

let initState = Map({ 
    files: List(), 
    displayMode: 'grid', 
    currentRequests: List() 
}); 

我有一个thunk方法,用于在发生事件(如进度更新)时开始上传和分派操作。例如,onProgress事件看起来是这样的:

onProgress: (data) => { 
    dispatch(fileUploadProgressUpdated({ 
     index, 
     progress: data.percentage 
    })); 
    } 

我使用redux-actions创建和处理我的行为,所以我对于行动减速是这样的:

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,所以终极版肯定不会有问题,我这样做......

,使用 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> 
    ); 
    } 
} 

因此,最终,如果我尝试下面的状态对象更新“进步”,反应,和/终极版没有更新我的组件:

{ 
    UploaderReducer: { 
     files: [{progress: 0}] 
    } 
} 

这是为什么?我认为使用Immutable.js的整个想法是,无论您更新它们的深度如何,比较修改后的对象都会更容易一些。

它似乎越来越普遍具有不可改变到Redux的工作并不简单,因为它似乎是: How to use Immutable.js with redux? https://github.com/reactjs/redux/issues/548

然而,使用不可变的吹捧好处似乎是值得这场战斗,我很乐意图我做错了什么!

UPDATE 2016年4月10日 选择的答案告诉我,我在做什么错的,为了完整起见,我updateFilePropsAtIndex功能现在包含简单:

return state.updateIn(['files', index], file => 
    Object.assign({}, file, fileProps) 
); 

这工作得很好! :)

+0

当你说“的组件没有被更新“是否意味着它不会从redux中接收新的道具? mapStateToProps是否未收到更新的进度?减速机如何,动作在有效载荷中是否有正确的进展? – azium

+0

UploaderContainer mapStateToProps函数根本不会触发,尽管在DevTools中使用期望值更新状态。我有单元测试检查我在做什么应该工作,所以我觉得在Redux级别有一些我不理解的东西,但我不知道它是什么! –

+0

你可以在JSBin网上找到一个工作示例吗?这将更容易解决。 – whitep4nther

回答

5

两个一般的想法第一:

  • Immutable.js是潜在有用的,是的,但你可以完成相同的数据不变的处理不使用它。有许多库可以帮助使不可变的数据更新更易于阅读,但仍然可以在普通对象和数组上运行。我在Redux相关的库回购中有很多列在Immutable Data页面上。
  • 如果React组件看起来没有更新,那几乎总是因为reducer实际上是在变更数据。 Redux FAQ对该主题有一个答案,在http://redux.js.org/docs/FAQ.html#react-not-rerendering。现在

,因为你使用Immutable.js,我承认数据的突变似乎有点不太可能。这就是说...减速机中的file[prop] = fileProps[prop]系列似乎非常好奇。你期望在那里发生什么?我会仔细看看那部分。

其实,现在我看着它......我几乎100%肯定你在改变数据。您的更新程序回调到state.updateIn(['files', index])将返回与您获取的参数完全相同的文件对象。每Immutable.js文档在https://facebook.github.io/immutable-js/docs/#/Map

如果更新函数返回它被称为具有相同的值,则不会发生更改。如果没有提供SetValue,这仍然是真的。

所以是的。您将返回与您获得的值相同的值,您的直接变化会显示在DevTools中,因为该对象仍处于悬停状态,但由于您返回了相同的对象,Immutable.js实际上并未实际返回任何修改的对象层次结构。因此,当Redux对顶级对象进行检查时,它看不到任何更改,不通知订户,因此组件的mapStateToProps从不运行。

清理你的reducer,并从更新器中返回一个新的对象,它应该应该都只是工作。

(A,而迟来的答案,但我刚才看到了问题,它似乎仍然是开放的,希望你实际上得到它固定到现在...)

+0

嘿,对不起,我没有回应。我目前正在升级React/Redux和其他模块。我会尽力在这个周末做这个,回到你身边。感谢您的详细解答! –

+1

我开始实施你指出的改变,它解决了所有问题。谢谢你的帮助! :) –

+0

@ChrisPaton你能分享你的相关变化吗? –