Redux:在规范化状态树中切换布尔值的最佳方式是什么?

Redux: What is the best way to toggle a boolean value in a normalized state tree?

我目前正在使用 React Native 开发应用程序。应用程序的状态非常复杂,但由于 Redux 和 Normalizr 可以管理。我现在必须为用户实现过滤项目的功能。

为了让用户过滤项目,我在 Normalizr 模式中丰富了服务器响应:

export const subCategorySchema = new schema.Entity(
  "subCategories",
  {},
  {
    idAttribute: "uuid",
    processStrategy: entity => {
      const newEntity = Object.assign({}, { name: entity.name, uuid: entity.uuid, chosen: false });
      return newEntity;
    }
  }
);

对应的reducer现在是这样的:

const initialState = {};
const subCategoriesReducer = (state = initialState, action) => {
  if (action.payload && action.payload.entities) {
    return {
      ...state,
      ...action.payload.entities.subCategories
    };
  } else {
    return state;
  }
};

这些子类别现在使用此 SwitchListItem 组件显示在 UI 中,该组件通过选择器获取其项目:

import React, { Component } from "react";
import { Switch, Text, View } from "react-native";
import PropTypes from "prop-types";

import styles, { onColor } from "./styles";

export default class SwitchListItem extends Component {
  static propTypes = {
    item: PropTypes.object
  };

  render() {
    const { name, chosen } = this.props.item;
    return (
      <View style={styles.container}>
        <Text style={styles.switchListText}>{name}</Text>
        <Switch style={styles.switch} value={chosen} onTintColor={onColor} />
      </View>
    );
  }
}

我现在要实现 <Switch /> component's onValueChange() 功能,这就是我的问题所在:

在规范化状态树中切换布尔值的最佳方式是什么?

我想出了两个解决方案,我将在下面描述。如果您认为其中任何一个不错,请告诉我。如果没有,我很想获得有关我可以做得更好的建议:)

解决方案 1:扩展减速器:

我对这个问题的第一个解决方案是扩展 reducer 以监听 TOGGLE_ITEM 动作。这看起来像这样:

const subCategoriesReducer = (state = initialState, action) => {
  switch (action.type) {
    case TOGGLE_ITEM:
      if (action.payload.item.uuid in state) return { ...state, ...action.payload.item };
  }
  if (action.payload && action.payload.entities) {
    return {
      ...state,
      ...action.payload.entities.subCategories
    };
  } else {
    return state;
  }
};

这是我的首选解决方案,因为它不需要很多代码。

解决方案 2:丰富将项目传递给 SwitchList 的选择器:

另一种解决方案是在使用带有状态键的选择器将对象传递到列表时丰富对象。然后我可以创建一个使用此键更新状态的操作,如下所示:

const toggleItem = (item, stateKey) => ({
  type: TOGGLE_ITEM,
  payload: {entities: { [stateKey]: item } }
})

如果您对 Redux 有丰富的经验,我很乐意阅读答案,最好是自以为是。另外,如果您认为我在 normalizr 中丰富数据的方法不好,您可以想出更好的方法,请告诉我!非常感谢您的任何建议!

我用完全不同的方式做到了。

我创建了一个数组来保存切换项目的 uuid。因此我只需要查看该项目是否在切换数组中。

就像这样:

const initialState = {};

export const byId = (state = initialState, action) => {
  if (action.payload && action.payload.entities && action.payload.entities.itemClassifications) {
    return {
      ...state,
      ...action.payload.entities.itemClassifications
    };
  } else {
    return state;
  }
};

export const chosen = (state = [], action) => {
  if (action.type === TOGGLE_ITEM && action.meta === ITEM_CLASSIFICATION) {
    if (state.includes(action.payload.uuid)) {
      return state.filter(uuid => uuid !== action.payload.uuid);
    } else {
      return [...state, action.payload.uuid];
    }
  } else {
    return state;
  }
};

const itemClassificationsReducer = combineReducers({
  byId,
  chosen
});

export default itemClassificationsReducer;

export const getAllItemClassificationsSelector = state =>
  Object.values(state.itemClassifications.byId);
export const getAllItemClassificationsNormalizedSelector = state => state.itemClassifications.byId;
export const getChosenItemClassificationsSelector = state => state.itemClassifications.chosen;

export const enrichAllItemClassificationsSelector = createSelector(
  getAllItemClassificationsSelector,
  itemClassifications =>
    itemClassifications.map(val => ({ ...val, stateKey: ITEM_CLASSIFICATION }))
);

export const getItemClassificationsFilterActiveSelector = createSelector(
  getChosenItemClassificationsSelector,
  itemClassifications => itemClassifications.length > 0
);