Javascript React.js:useffect()依赖项冻结我的应用程序
我试图在用户以前未选择任何产品时显示模式。我最终得到了一个无限的Javascript React.js:useffect()依赖项冻结我的应用程序,javascript,reactjs,react-hooks,use-effect,Javascript,Reactjs,React Hooks,Use Effect,我试图在用户以前未选择任何产品时显示模式。我最终得到了一个无限的useffect()依赖循环。我不知道如何正确地做这件事 import React, { useState, useEffect, useCallback } from 'react'; const MyComponent = ({ products }) => { const [modals, setModals] = useState({}); const [currentModalName, setCu
useffect()
依赖循环。我不知道如何正确地做这件事
import React, { useState, useEffect, useCallback } from 'react';
const MyComponent = ({ products }) => {
const [modals, setModals] = useState({});
const [currentModalName, setCurrentModalName] = useState('');
const setCurrentModal = useCallback(
(modalName, data = {}) => {
if (modalName) {
setModals({
...modals,
[modalName]: {
...modals[modalName],
...data
}
});
}
setCurrentModalName(modalName);
},
[modals]
);
useEffect(
() => {
if (!products.length) {
setCurrentModal('chooseProduct')
}
},
[products, setCurrentModal] // setCurrentModal causes infinite loop
);
return (
<div>...</div>
);
}
export default MyComponent;
import React,{useState,useffect,useCallback}来自'React';
常量MyComponent=({products})=>{
const[modals,setModals]=useState({});
常量[currentModalName,setCurrentModalName]=useState(“”);
const setCurrentModal=useCallback(
(modalName,data={})=>{
if(modalName){
设置模态({
…情态动词,
[modalName]:{
…模态[模态名称],
…数据
}
});
}
setCurrentModalName(modalName);
},
[情态动词]
);
使用效果(
() => {
如果(!products.length){
setCurrentModal('chooseProduct')
}
},
[产品,setCurrentModal]//setCurrentModal导致无限循环
);
返回(
...
);
}
导出默认MyComponent;
我可以从依赖项中删除setcurrentmodel
,但是我收到了警告。如果我添加它,我的React应用程序将冻结
如何组织代码以避免冻结?为什么会循环?
回调总是在变化,因为它依赖于modals
,而modals始终是一个不同的对象,即使它具有与以前完全相同的属性,它总是触发useffect
,因为它依赖于setCurrentModal
回调值,而回调值总是不同的,因为(()=>{})!==(()=>{})
解决方案
需要当前状态时始终使用设置下一个状态
它将防止将modals
状态作为依赖项使用,这将限制回调的更新时间,同时修复无限循环
除了解决今天的问题外,状态的功能更新不太容易出现争用情况,React批处理的多个更新会相互覆盖
const setCurrentModal = useCallback(
(modalName, data = {}) => {
if (!modalName) return; // precondition fail? early return.
// Happy path here!
// Note that I've used a different name to highlight that
// it's a different variable and to avoid shadowing the
// `modals` var from the outer scope.
setModals((currentModals) => ({ // use functional update.
...currentModals,
[modalName]: {
...currentModals[modalName],
...data
}
}));
setCurrentModalName(modalName);
},
// remove `modals` from the dependencies.
// setter functions are stable anyway, so it should remove any warning.
[setModals, setCurrentModalName]
);
useEffect(() => {
if (!products.length) {
setCurrentModal('chooseProduct')
}
},
[products, setCurrentModal]
);
由于setCurrentModal
回调现在是稳定的(从未更改),因此只有当products
值更改时才会调用useffect
缺少依赖项警告
缺少依赖项警告来自react hooks/deps
规则。它是完全可选的,但它有助于保持代码干净和安全
您还可以选择仅针对此行禁用警告:
const setCurrentModal = useCallback(
(modalName, data = {}) => {
// ...
setModals(/* ... */);
setCurrentModalName(modalName);
},
[] // eslint-disable-line react-hooks/exhaustive-deps
);
我认为您可以简化它,而无需使用useCallback (使用Next.js进行测试,没有警告,但如果仍然有警告,则应使用@Emile Bergeron的答案)
相关:但是我在我的组件中使用这种更改模态的逻辑。只是用外部化函数编辑了代码警告仍然存在。是的,只是用useCallback编辑了帖子以防止警告问题是,在这种情况下,我仍然被警告缺少
setCurrentModal
依赖项。我认为这是因为它位于功能组件内部,所以即使它是常量
,它仍将在下一个周期中更改。我可以将setcurrentmodel
移动到组件外部,除了is使用状态。@RoboRobok我已经用所有相关的依赖项更新了我的答案,这些依赖项将删除任何警告并修复循环问题。它说modals
也是必需的依赖项,不幸的是。@RoboRobok这意味着modals
仍在回调或效果中使用,并且您没有正确实现我的答案中的内容(这将从回调中完全删除modals
状态)。非常感谢!很抱歉,一开始我忽略了你答案的某些部分,我感到不知所措。谢谢你的回答。
import React, { useState, useEffect } from 'react'
const MyComponent = ({ products }) => {
const [modals, setModals] = useState({})
const [currentModalName, setCurrentModalName] = useState('')
const setCurrentModal = (name, data) => {
if (name) {
setModals(prev => {
return { ...prev, [name]: { ...prev[name], ...data }}
})
setCurrentModalName(name)
}
}
useEffect(() => {
if (!products || !products.length) {
const modalName = 'chooseProduct'
const data = { data: 'data' }
setCurrentModal(modalName, data)
}
}, [products])
const modalsJsx = modals ? Object.keys(modals).map((x, i) => {
return <li key={`modal-${i}`}>{x}</li>
}) : ''
const addModal = () => {
const name = 'test' + Math.floor(Math.random() * Math.floor(300))
setCurrentModal(name, { data: 'Hey' })
}
return (
<div>
<p>Current Modal : {currentModalName}</p>
<p>Modals : </p>
<ul>
{modalsJsx}
</ul>
<button onClick={addModal}>Test</button>
</div>
)
}
export default MyComponent
const setCurrentModal = useCallback((name, data = {}) => {
if (name) {
setModals(prev => {
return { ...prev, [name]: { ...prev[name], ...data }}
})
setCurrentModalName(name)
}
}, [setModals, setCurrentModalName])