1

Sorry for the long text... I'm trying to figure out the best way to chain some actions which need to wait for each other for calculating the initial state. Probably reselect would help, but I'm having a hard time figuring out how to fit it in. I am using ImmutableJS, but I'll use standard JSON to describe my troubles. Initial state should be like:

{
"book": {
"id": 1,
"title": "THE book",
"notes": [
  1,
  2,
  4
]
 },
 "detailedNotes": [
{
  "id": 1,
  "text": "Just a note",
  "categories": [
    "Vital"
  ]
},
{
  "id": 2,
  "text": "Testing",
  "categories": [
    "Vital"
  ]
},
{
  "id": 4,
  "text": "Doodling",
  "categories": [
    "Relevant"
  ]
}
],
"notesByCategory": [
{
  "Vital": [
    1,
    2
   ]
 },
 {
  "Relevant": [
    4
  ]
 }
 ],
 "activeCategory": "Vital"
}

book and detailedNotes are from a GET call (using fetch). notesByCategory is calculated by my code (at the moment in the reducer...) and I also need to set the initial value for activeCategory as the first calculated category (Vital in the example). Here's the actions.js and reducer.js relevant code:

        //in actions
    export function fetchBook(id) {
        return function (dispatch) {
            return fetch('/api/book/' + id)
                .then(json => {
                        dispatch(receiveBook(json));
                        dispatch(fetchDetailedNotes(json['notes']));
                    }
                );
        }
    }
    export function fetchDetailedNotes(ids) {
        return function (dispatch) {
            return fetch('/api/notes/?ids=' + ids.join())
                .then(json => dispatch(receiveDetailedNotes(json)))
        }
    }
    export function receiveDetailedNotes(detailedNotes) {
        return {
            type: RECEIVE_DETAILED_Notes,
            detailedNotes
        }
    }

    //in reducer
    function detailedNotesReducer(state = List(), action) {
        switch (action.type) {
            case actions.RECEIVE_DETAILED_NOTES:
                return fromJS(action.detailedNotes);
            default:
                return state
        }
    }
    function notesByCategory(state = List(), action) {
        switch (action.type) {
            case actions.RECEIVE_DETAILED_NOTES:
                return extractNotesByCategory(state, action.detailedNotes);
            default:
                return state
        }
    }

There's reducer composition also, I omitted it. Now, I want to set the initial activeCategory to the first category, but I have a problem, I have NO idea where to put this. I have to wait first to have the notesByCategory part of the state calculated. I feel that this calculation should not be maybe in the reducer. Any suggestions?

4

2 回答 2

2

您可以通过不在您的状态中存储派生数据来避免这种情况,而是使用选择器计算它(尽管您不一定必须为此使用重新选择)。

选择器实际上只是一个函数,它接受通过 mapStateToProps 传入的状态并派生容器组件作为道具获得的内容。

您的选择器可以访问整个商店,因此在返回 activeCategory 的选择器中,您可以有一些逻辑返回商店中的activeCategory(如果不是null)或一些基于可用类别的默认值(如果null)。

将您的商店视为“事实来源”并使用选择器从那里获取您需要的所有内容具有防止不一致的额外好处,因为您可以确信商店是正确的并且独立于其他数据。

于 2016-05-10T14:42:11.910 回答
0

一般来说,默认状态应该由你的 reducer 处理。在开始时,您的减速器中的状态为空,您应该提供一些“初始”状态。

在您的情况下,这通常是通过默认参数 ( =List()) 的便捷语法使用的。

于 2016-05-10T10:13:41.697 回答