11

我使用带有 normalizr 的 redux 来规范来自服务器的响应,基本上遵循真实世界的示例。这种方式entitiesreducer很简单,只需要合并响应即可。我现在遇到的问题是一种delete操作。我发现了 normalizr repo 的这个 issue#21,但仍然不知道如何解决这个问题。例如,

当前状态是

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

标准化响应是

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

如您所见,后端 api 仅返回属于该类别的所有产品 id,在这种情况下,“2”是分离的。当 'entities' reducer 合并这个响应时,“2”仍然存在。现在我只是重新加载页面,但我想知道是否有更好的方法来处理这种情况?

entitiesreducer 中,我只是像在现实世界的例子中一样合并它。

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

4

3 回答 3

11

只是不要担心它在那里。把你的状态想象成一个数据库。您也不会真正从数据库中删除记录以避免复杂的级联——通常您只是更改它们在数据库中的状态。同样,使用 Normalizer,不是真正删除实体,而是让它们在缓存中直到用户离开页面!

于 2015-11-18T23:27:06.807 回答
0

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

要执行删除,我已将减速器更新为处理删除操作:REMOVE_ENTITY_ITEM。在操作中,我传入要删除的实体的idand 。name

在减速器中,我首先删除位于的实体本身store.entities[entityName][entityId]。然后接下来我需要id从可能引用它的所有其他实体中删除它。由于我使用normalizr的所有实体都是平面的,如果它们引用另一个实体,那么它们只会在数组中具有它的 id。这使得删除引用相对简单。我只是遍历所有实体并过滤掉对被删除实体的引用。

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

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"
  }
});
于 2016-11-14T16:02:50.490 回答
0

使用 lodash 分配。如果你使用合并将不起作用。

例子:

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>

于 2018-04-19T13:10:35.657 回答