我有以下 onSelect 函数,包装在 useCallback...
const [descriptive, setDescriptive] = useState({})
const handleSelectionsCall = useCallback((event, inputLabel) => {
const { target: { value }} = event;
var newVal = typeof value === "string" ? value.split(",") : value;
const lastItem = newVal[newVal.length -1]
if (lastItem === 'Unselect') newVal = []
const update = descriptive[inputLabel];
const updatedDescriptive = {
...descriptive,
[inputLabel]: {
...update,
selected: newVal,
error: newVal.length === 0 && ["Exchange", "Industry", "Sector"].includes(inputLabel) ? "At least one selection must be made" : ""
}
};
setDescriptive(updatedDescriptive);
}, [descriptive, setDescriptive]);
我将其传递给这三个下拉检查选择 Material UI 组件。
<div className={colSize}>
<CheckSelect
selected={descriptive.Sector.selected}
allSelections={allSectors}
allSelected={
descriptive.Sector.selected.length === allSectors.length
}
inputLabel="Sector"
onSelect={handleSelectionsCall}
labelFontSize={"120%"}
extraOutlineSpace={"fil"}
validationWarning={descriptive.Sector.error}
size={smallWidth ? "small" : "medium"}
/>
</div>
<div className={colSize}>
<CheckSelect
selected={descriptive.Industry.selected}
allSelections={descriptive.Industry.allSelections}
allSelected={
descriptive.Industry.selected.length === descriptive.Industry.allSelections.length
}
inputLabel="Industry"
onSelect={handleSelectionsCall}
labelFontSize={"120%"}
extraOutlineSpace={"fill"}
validationWarning={descriptive.Industry.error}
size={smallWidth ? "small" : "medium"}
/>
</div>
<div className={colSize}>
<CheckSelect
selected={descriptive.Exchange.selected}
allSelections={exchanges}
allSelected={descriptive.Exchange.selected.length === 2}
inputLabel="Exchange"
onSelect={handleSelectionsCall}
labelFontSize={"120%"}
extraOutlineSpace={"filll"}
validationWarning={descriptive.Exchange.error}
size={smallWidth ? "small" : "medium"}
/>
</div>
CheckSelect 组件已被记忆(以下是完整组件的简化版本)
export default memo(function CheckSelect({
allSelections,
selected,
onSelect,
inputLabel='',
labelFontSize,
extraOutlineSpace='',
validationWarning='test',
size,
allSelected=false
}) {
console.log('render ' + inputLabel)
return (
<Select
labelId="demo-multiple-checkbox-label"
id="demo-multiple-checkbox"
sx={selectFieldStyles(validationWarning)}
multiple
value={selected}
onChange={e => onSelect(e, inputLabel)}
input={<OutlinedInput label={inputLabel+extraOutlineSpace} />}
renderValue={(selected) => {
if (allSelected) return 'Any'
return selected.join(', ')
}}
MenuProps={MenuProps}
>
{map selections}
</Select>
)
})
当我更改其中一个选择时,其他两个选择会重新渲染,即使它们的道具没有改变。为什么会出现这种情况?
我知道这与handleSelectionsCall有关,因为如果我将其注释掉,以免传递给CheckSelect,则当更新另一个CheckSelect时,其他CheckSelect不会重新呈现。
谢谢!
问题是您已将
descriptive
放入依赖项列表中,并且您的函数始终调用 setDescriptive
,这会更新 descriptive
,这意味着必须创建一个新函数。所以 useCallback
没有任何有用的目的。
为了避免这种情况,不要直接在函数中使用状态成员
descriptive
;使用 setDescriptive
的回调形式来代替:
const handleSelectionsCall = useCallback(
(event, inputLabel) => {
const {
target: { value },
} = event;
const newVal = typeof value === "string" ? value.split(",") : value;
const lastItem = newVal[newVal.length - 1];
if (lastItem === "Unselect") {
newVal = [];
}
setDescriptive((descriptive) => { // ***
const update = descriptive[inputLabel];
const updatedDescriptive = {
...descriptive,
[inputLabel]: {
...update,
selected: newVal,
error:
newVal.length === 0 &&
["Exchange", "Industry", "Sector"].includes(
inputLabel
)
? "At least one selection must be made"
: "",
},
};
return updatedDescriptive;
});
},
[] // ***
);
请注意,我已从依赖项列表中删除了
descriptive
和 setDescriptive
,因为:
descriptive
- 函数不再使用它,而是使用回调的参数。setDescriptive
- 来自 useState
的状态设置器是 稳定,因此没有理由将它们列在依赖项列表中。现在,你的
handleSelectionsCall
已经稳定了,不会导致不必要的重新渲染。