gpt4 book ai didi

reactjs - react : Prevent infinite Loop when calling context-functions in useEffect

转载 作者:行者123 更新时间:2023-12-04 16:39:18 27 4
gpt4 key购买 nike

在我的 react 应用程序中,我正在渲染 <Item> 的不同实例组件,我希望它们在上下文中注册/取消注册,具体取决于它们当前是否已安装。
我正在使用两个上下文(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再次执行。
  • 还执行了先前渲染的清理效果! (如 here 所述:“React 还会在下次运行效果之前清除上一次渲染中的效果”)
  • 这又改变了items在上下文中
  • 等等...

  • 我真的想不出一个干净的解决方案来避免这个问题。我是否滥用了任何钩子(Hook)或上下文 api?关于如何编写由组件在其 useEffect 中调用的注册/取消注册上下文的任何一般模式,您能帮我吗? -body 和 useEffect -清理?
    我不想做的事情:
  • 完全摆脱上下文。在我的真实应用中,结构更复杂,整个应用中的不同组件都需要这些信息,所以我相信我想坚持一个上下文
  • 避免硬移除 <Item>来自 dom 的组件(使用 {renderFirstBlock && )并使用类似状态 isHidden 的东西反而。在我真正的应用程序中,这是目前我无法改变的。我的目标是跟踪 data所有现有组件实例。

  • 谢谢!

    最佳答案

    你可以让你的 setter 有稳定的引用,因为他们真的不需要依赖 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>
    );
    }
    现在您的效果应该按预期工作

    关于reactjs - react : Prevent infinite Loop when calling context-functions in useEffect,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64133238/

    27 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com