在我的react应用中,我正在渲染<Item>
组件的不同实例,并且希望它们在Context中注册/注销,具体取决于当前是否已安装它们。
我使用两个上下文(ItemContext
提供了注册的项目,ItemContextSetters
提供了注册/注销的功能)。
const ItemContext = React.createContext({});
const ItemContextSetters = React.createContext({
registerItem: (id, data) => undefined,
unregisterItem: (id) => undefined
});
function ContextController(props) {
const [items, setItems] = useState({});
const unregisterItem = useCallback(
(id) => {
const itemsUpdate = { ...items };
delete itemsUpdate[id];
setItems(itemsUpdate);
},
[items]
);
const registerItem = useCallback(
(id, data) => {
if (items.hasOwnProperty(id) && items[id] === data) {
return;
}
const itemsUpdate = { ...items, [id]: data };
setItems(itemsUpdate);
},
[items]
);
return (
<ItemContext.Provider value={{ items }}>
<ItemContextSetters.Provider value={{ registerItem, unregisterItem }}>
{props.children}
</ItemContextSetters.Provider>
</ItemContext.Provider>
);
}
<Item>
组件在挂载或props.data
更改时应自行注册,而在卸载时应注销。因此,我认为可以使用useEffect
非常干净地完成此操作:
function Item(props) {
const itemContextSetters = useContext(ItemContextSetters);
useEffect(() => {
itemContextSetters.registerItem(props.id, props.data);
return () => {
itemContextSetters.unregisterItem(props.id);
};
}, [itemContextSetters, props.id, props.data]);
...
}
完整示例请参见此codesandbox
现在,问题在于这给了我无限循环,我不知道如何做得更好。循环是这样发生的(我相信):
<Item>
个电话registerItem
items
被更改,因此registerItem
被重新构建(因为它取决于[items]
<Item>
中的更改,因为itemContextSetters
已更改,并且useEffect
被再次执行。items
我真的想不出一个可以避免此问题的干净解决方案。我是否滥用任何挂钩或上下文API?您可以通过任何一般模式帮助我如何编写由组件在其useEffect
主体和useEffect
-cleanup中调用的这种注册/注销上下文吗?
我不想做的事情
<Item>
组件从dom中强制删除(使用{renderFirstBlock &&
),而应使用状态isHidden
之类的东西。在我的真实应用中,这是我目前无法更改的任何内容。我的目标是跟踪所有现有组件实例的data
。谢谢!
答案 0 :(得分:2)
您可以使设置器具有稳定的引用,因为它们确实不需要依赖items
:
const ItemContext = React.createContext({});
const ItemContextSetters = React.createContext({
registerItem: (id, data) => undefined,
unregisterItem: (id) => undefined
});
function ContextController(props) {
const [items, setItems] = useState({});
const unregisterItem = useCallback(
(id) => {
setItems(currentItems => {
const itemsUpdate = { ...currentItems };
delete itemsUpdate[id];
return itemsUpdate
});
},
[]
);
const registerItem = useCallback(
(id, data) => {
setItems(currentItems => {
if (currentItems.hasOwnProperty(id) && currentItems[id] === data) {
return currentItems;
}
return { ...currentItems, [id]: data }
} );
},
[]
);
const itemsSetters = useMemo(() => ({ registerItem, unregisterItem }), [registerItem, unregisterItem])
return (
<ItemContext.Provider value={{ items }}>
<ItemContextSetters.Provider value={itemsSetters}>
{props.children}
</ItemContextSetters.Provider>
</ItemContext.Provider>
);
}
现在您的效果应该可以预期了