在 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;
}
);
经过更多阅读,这终于对我有用了。希望有人会发现它有用。
- 给定规范化数据,可以简化对象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;
}
);
- 为了防止re-render,我们可以在输出数组上使用shallowEqual比较来只比较数组的值。
//app.js
import { shallowEqual, useSelector } from "react-redux";
const childrenTextData = useSelector((state) => {
return getChildrenByFieldId(state, blockId);
}, shallowEqual);
我有一个简单的文档制作器,允许用户创建两种类型的多个字段(输入字段和标签字段)。他们还可以通过拖放重新排列字段。我的数据结构基于数据规范化的 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;
}
);
经过更多阅读,这终于对我有用了。希望有人会发现它有用。
- 给定规范化数据,可以简化对象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;
}
);
- 为了防止re-render,我们可以在输出数组上使用shallowEqual比较来只比较数组的值。
//app.js
import { shallowEqual, useSelector } from "react-redux";
const childrenTextData = useSelector((state) => {
return getChildrenByFieldId(state, blockId);
}, shallowEqual);