将reducer绑定到状态的子集,动态设置

时间:2017-03-02 15:00:07

标签: javascript redux state react-redux redux-thunk

我目前正在使用Redux,我不清楚将reducer绑定到sub,动态设置状态的正确方法是什么。

例如,假设我的状态看起来像这样(在从后端API异步获取一些数据之后)

{
   "categories": {
      "collection": {
        "42": {
           "id": "42",
           "title": "whatever",
           "owner_id": "10"
           "posts": {
              "collection": {
                "36": {
                   "id": "36", 
                   "title": "hello",
                   "content": "hello world"
                },
                "37": { // more posts ... } 
              },
              "ids": ["36", "37", ...]
           },
           "otherChildren": { // more sub-entities } 
        },
        "43": { // more categories ... }
      },
      "ids": ["42", "43", ...]
   },
   "users": {
     "collection": {
       "10": {
          "id": "10"
          "email": "what@ever.com"
       },
       "11": { // more users ... } 
     },
     "ids": [10, 11]
   }
}

我的根减速器看起来像这样:

export default combineReducers({
    categories: categoriesReducer,  
    users: usersReducer
})

和categoriesReducer:

function categoriesReducer(state = initialState, action) {
  switch (action.type) {
    case FETCH_ALL_CATEGORIES_SUCCESS:
      return Object.assign({}, state, {
        categories: { 
          collection: action.payload 
        } 
      })
    default:
      return state
  }
}

现在我要做的是使用postsReducer函数无缝地委托处理状态的post子集部分,基本上添加一个例如:

  case FETCH_CATEGORY_ALL_POSTS_SUCCESS:
    let categoryId = action.categoryId 
    return Object.assign({}, state, {
      categories: {
        [categoryId]: combineReducers({
           "posts": postsReducer,
           "otherChildren": otherChildrenReducer
        })
      }
    }

当然,这不起作用。我没有得到的是如何让redux使用combineReducer为嵌套的reducer自动更新状态的子集,同时自动将正确的子集作为state参数传递给reducer函数,而不会覆盖现有数据(即我的例子。)

我设法让这项工作编写了我自己的“委托”功能,但感觉非常错误 - 特别是看https://github.com/reactjs/redux/blob/master/src/combineReducers.js看起来就像这样做。

传统上,我怎么想这样做?甚至可以使用combineReducers与Redux一样,我是否误解了combineReducer的观点,还是我期待它的魔力呢?

谢谢!

修改/更新:

我确实需要嵌套的那些(对,也许category / post示例不正确)并且我想要减速器(即这里,postsReducer,但它可以是Collection缩减器,可以在多个地方重复使用。

为什么我希望它嵌套?实际上在这个例子中,假设一个post只能属于一个category,因为{{{ 1}}实际上是使用来自post的私钥加密的。这就是为什么代表这个链,这个关系在状态中对我来说非常敏感)

在传递状态的正确子集时,还没有办法让redux委托给其他reducer - 例如,将状态category传递给postReducer吗?

2 个答案:

答案 0 :(得分:0)

你在商店这么好的地方,为什么不继续分离所有不同的收藏品。

我的意思是你的帖子应该/可能有另一个减速器,商店会像这样:

{
   categories: [],
   posts: [],
   users: []
}

您的类别将仅包含帖子的ID

所以你可以在两个reducer(类别和新帖子reducer)中“捕获”“FETCH_ALL_CATEGORIES_SUCCESS”动作,在categoryReducer上你可以保护类别数据和ID,而postsReducer只会保存帖子

答案 1 :(得分:0)

您在上一个代码段中以错误的方式使用combineReducers。 (在您使用它将categoriesReducer和usersReducer组合在一起的第一个片段中是正确的。)

对于FETCH_CATEGORY_ALL_POSTS_SUCCESS,不要调用combineReducers,只需调用categoriesReducer中的普通函数。

posts: posts(action.payload.posts)

普通功能基本上是一个减速器。这是你自己写的,你可能把它放在同一个文件中。

但上面代码的另外两个主要问题是:

1)正如另一位用户已经说过的那样,将帖子存储为类别的子属性可能不是最好的。而是将帖子存储为状态树中的自己的项目,并且每个帖子只有一个category_id字段以显示它属于哪个类别。因此,在这种情况下,您的状态树将如下所示:

{
  categories: {},
  posts: {},
  users: {}
}

你最初会将combineReducers用作:

export default combineReducers({
  categories: categoriesReducer,  
  posts: postsReducer,
  users: usersReducer
})

2)在categoriesReducer(或任何相关内容)中,您不必拥有categories属性,因为您在调用combineReducers时已经创建了该属性。换句话说,在categoriesReducer你应该只是这样:

function categoriesReducer(state = initialState, action) {
  switch (action.type) {
    case FETCH_ALL_CATEGORIES_SUCCESS:
      return Object.assign({}, state, {
        collection: action.payload,
        ids: // function to extract ids goes here
      });
    default:
      return state;
  }
}