首页 文章

Redux Normalizr分离(删除)操作

提问于
浏览
11

我使用redux和normalizr来规范化服务器的响应,基本上遵循real-world示例 . 这种方式 entities reducer非常简单,只需合并响应即可 . 我现在遇到的问题是 delete 操作 . 我发现这个issue#21 of normalizr repo但仍然无法弄清楚如何解决这个问题 . 例如,

目前的状态是

{
  entities:
    product_categories: {
      ...
      13: {
        ...
        products: ["1", "2"], <--------------- [i] Current state
        ...
      }
    },
    products: {
      1: {
        id: "1"
      }
    }
}

标准化的响应是

{
  ...
    product_categories: {
      ...
      13: {
        ...
        products: ["1"], <---------------- [2] Normalized result
      }
  ...
}

如您所见,后端api只返回 all product ids that belonged to this category ,在这种情况下"2"已分离 . 当'entities' reducer合并此响应时,"2"仍然悬而未决 . 现在我只是重新加载页面,但我是一个更好的方法来处理这种情况?

entities reducer中,我只是像现实世界中的例子一样合并它 .

return merge({}, state, action.payload.entities);

3 回答

  • 0

    只是不要担心它在那里 . 将您的状态视为数据库 . 您不会真正从数据库中删除记录,以避免复杂的级联 - 通常您只需更改其在数据库中的状态 . 类似地,使用Normalizer,而不是真正删除实体,让它们在缓存中,直到用户离开页面!

  • 12

    下面是我的解决方案后面的代码解释 .

    要执行删除,我已将我的reducer更新为处理删除操作: REMOVE_ENTITY_ITEM . 在操作中,我传递了要删除的实体的 idname .

    在reducer中,我首先删除 store.entities[entityName][entityId] 处的实体本身 . 接下来我需要将其 id 从可能引用它的所有其他实体中删除 . 因为我使用 normalizr 所有实体都是扁平的,如果它们引用另一个实体,那么它们只会在数组中有它的id . 这使得删除引用相对简单 . 我只是循环遍历所有实体并过滤掉对要删除的实体的引用 .

    我将此方法与#1的其他两种方法结合使用 . )刷新app / state和#2 . )翻转实体状态位而不是删除然后过滤UI中关闭的项目 . 这些方法已得到很好的讨论here

    const entities = (state={}, action) => {
      if(action.payload && action.payload.entities) {
        return merge( {} , state, action.payload.entities);
      }else{
        return deleteHandlingReducer(state, action)
      }
    }
    
    const deleteHandlingReducer = (state=initialSate, action) => {
      switch(action.type){
        case "REMOVE_ENTITY_ITEM":
          if (!action.meta || !action.meta.name || !action.meta.id) {
            return state;
          }else{
            let newState = Object.assign({}, state);
            if(newState[action.meta.name]){
              delete newState[action.meta.name][action.meta.id];
              Object.keys(state).map(key => {
                let entityHash = state[key];
                Object.keys(entityHash).map(entityId => {
                  let entity = entityHash[entityId];
                  if(entity[action.meta.name] &&
                    Array.isArray(entity[action.meta.name])){
                      entity[action.meta.name] = entity[action.meta.name].
                        filter(item => item != action.meta.id)
                  }
                });
              })
            }
            return newState;
          }
        default:
          return state;
      }
    }
    

    现在要删除我触发这样的动作:

    store.dispatch({
      type: "REMOVE_ENTITY_ITEM",
      meta: {
        id: 1,
        name: "job_titles"
      }
    });
    
  • 0

    使用lodash assign . 如果使用合并将无法正常工作 .

    例:

    const object = {
      'a': [1,2,3]
    }
    
    const other = {
      'a': [1,2]
    }
    
    // item 3 was not removed
    const merge = _.merge({}, object, other)
    console.log(merge) // {'a': [1,2,3]}
    
    // item 3 was removed
    const assign = _.assign({}, object, other)
    console.log(assign) // {'a': [1,2]}
    
    <script src="https://cdn.jsdelivr.net/npm/lodash@4.17.5/lodash.min.js"></script>
    

    在这里,您的代码将如何

    // state
    const state = {
     entities: {
       product_categories: {
         '1': {
           id: 1,
           products: [1,2]
         }
       },
       products: {
         '1': {
           id: 1
         },
         '2': {
           id: 2
         }
       }
     }
    }
    
    // example of action
    // normalized by normalizr
    const action = {
      type: 'REMOVE_PRODUCT_CATEGORY_SUCCESS',
      payload: {
        entities: {
          product_categories: {
            '1': {
               id: 1,
               products: [1]
            }
          }
        }
      }
    }
    
    // product_categories entity reducer
    function productCategoriesEntityReducer (state = {}, action) {
      switch (action.type) {
    
        default:
          if (_.has(action, 'payload.entities.product_categories')) {
            return _.assign({}, state, action.payload.entities.product_categories)
          }
          return state
      }
    }
    
    // run reducer on state.entities.product_categories
    const newState = productCategoriesEntityReducer(state.entities.product_categories, action);
    console.log(newState)
    
    <script src="https://cdn.jsdelivr.net/npm/lodash@4.17.5/lodash.min.js"></script>
    

相关问题