在 Redux 中创建一个不重新渲染不相关组件的 createSelector

Creating a createSelector in Redux that does not rerender unrelated components

我有一个简单的文档制作器,允许用户创建两种类型的多个字段(输入字段和标签字段)。他们还可以通过拖放重新排列字段。我的数据结构基于数据规范化的 Redux 指南。

LIST 字段是输入类型(例如子 ID 21 和 22)- onChange 将调度操作并允许修改输入文本而无需重新呈现其他字段。

使用 LABEL 字段,我们允许用户从只有 3 个标签(危险、湿滑、易燃)的下拉列表中选择 select。选择一个并从下拉列表中将其删除以防止标签重复。例如,如果字段中有“Danger”和“Slippery”,则下拉字段将只显示一个选项“Flammable”。为此,我使用 createSelector 获取所有子项并根据其父项 (fieldId) 过滤它们。这为我提供了该 fieldId 中现有标签 ["Danger","Slippery"] 的数组。然后,该数组将用于从具有 useEffect 的三个选项的固定下拉数组中进行过滤。

现在每当我更新输入文本时,它还会重新呈现 LABEL 字段(基于 Chrome React 分析器)。

它不会影响性能,但我觉得我的 createSelector 缺少了一些东西。

Example:

export const documentSlice = createSlice({
  name: "document",
  initialState: {
    fields: {
      1: { id: 1, children: [11, 12] },
      2: { id: 2, children: [21, 22] },
    },
    children: {
      11: { id: 11, type: "LABEL", text: "Danger", fieldId: 1 },
      12: { id: 11, type: "LABEL", text: "Slippery", fieldId: 1 },
      21: { id: 21, type: "LIST", text: "", fieldId: 2 },
      22: { id: 22, type: "LIST", text: "", fieldId: 2 },
    },
    fieldOrder:[1,2]
  },
});

创建选择器

export const selectAllChildren = (state) => state.document.children;
export const selectFieldId = (state, fieldId) => fieldId;

export const getChildrenByFieldId = createSelector(
  [selectAllChildren, selectFieldId],
  (children, fieldId) => {
    const filterObject = (obj, filter, filterValue) =>
      Object.keys(obj).reduce(
        (acc, val) =>
          obj[val][filter] !== filterValue ? acc : [...acc, obj[val].text],
        []
      );
    const existingChildren = filterObject(children, "fieldId", filterId);
    return existingChildren;
  }
);

经过更多阅读,这终于对我有用了。希望有人会发现它有用。

  1. 给定规范化数据,可以简化对象reduce函数。
slice.js 

export const getChildrenByFieldId = createSelector(
  [selectAllChildren, selectFieldId],
  (children, fieldId) => {

    // get the children from fields entry => array [11,12]
    const childrenIds = state.document.fields[fieldId].children

    let existingChildrenText = [];

    // loop through the created array [11,12] 
    childrenIds.forEach((childId) => {
      // pull data from the individual children entry
      const childText = children[childId].text;
      existingChildrenText.push(childText);
    });
    return existingChildrenText;

  }
);
  1. 为了防止re-render,我们可以在输出数组上使用shallowEqual比较来只比较数组的值。
//app.js 

import { shallowEqual, useSelector } from "react-redux";

const childrenTextData = useSelector((state) => {
  return getChildrenByFieldId(state, blockId);
}, shallowEqual);