Tob*_*gen 45 reactjs react-hooks
我一直在使用React 16.7-alpha中的新钩子系统,当我正在处理的状态是一个对象或数组时,它会在useEffect中陷入无限循环.
首先,我使用useState并使用如下的空对象启动它:
const [obj, setObj] = useState({});
Run Code Online (Sandbox Code Playgroud)
然后,在useEffect中,我使用setObj将其再次设置为空对象.作为第二个参数,我传递[obj],希望如果对象的内容没有改变,它不会更新.但它不断更新.我想因为无论内容如何,这些都是不同的对象,让React认为它在不断变化?
useEffect(() => {
setIngredients({});
}, [ingredients]);
Run Code Online (Sandbox Code Playgroud)
数组也是如此,但作为一个原语,它不会像预期的那样卡在循环中.
使用这些新的钩子,在检查天气内容是否发生变化时,我应该如何处理对象和数组?
Tob*_*gen 49
将空对象或空数组作为useEffect的第二个参数传递使得它仅在mount和unmount上运行,从而停止任何无限循环.
useEffect(() => {
setIngredients({});
}, []);
Run Code Online (Sandbox Code Playgroud)
在https://www.robinwieruch.de/react-hooks/上的React hooks博客文章中向我澄清了这一点.
RTW*_*RTW 40
有同样的问题。我不知道他们为什么不在文档中提及这一点。只是想在Tobias Haugen答案中添加一些内容。
要在每个组件/父渲染中运行,您需要使用:
useEffect(() => {
// don't know where it can be used :/
})
Run Code Online (Sandbox Code Playgroud)
要在组件安装后仅运行一次(将渲染一次),您需要使用:
useEffect(() => {
// do anything only one time if you pass empty array []
// keep in mind, that component will be rendered one time (with default values) before we get here
}, [] )
Run Code Online (Sandbox Code Playgroud)
要一次在组件安装和数据/ data2更改上运行任何内容:
const [data, setData] = useState(false)
const [data2, setData2] = useState('default value for first render')
useEffect(() => {
// if you pass some variable, than component will rerender after component mount one time and second time if this(in my case data or data2) is changed
// if your data is object and you want to trigger this when property of object changed, clone object like this let clone = JSON.parse(JSON.stringify(data)), change it clone.prop = 2 and setData(clone).
// if you do like this 'data.prop=2' without cloning useEffect will not be triggered, because link to data object in momory doesn't changed, even if object changed (as i understand this)
}, [data, data2] )
Run Code Online (Sandbox Code Playgroud)
我大多数时候如何使用它:
export default function Book({id}) {
const [book, bookSet] = useState(false)
useEffect(() => {
loadBookFromServer()
}, [id]) // every time id changed, new book will be loaded
// Remeber that it's not always safe to omit functions from the list of dependencies. Explained in comments.
async function loadBookFromServer() {
let response = await fetch('api/book/' + id)
response = await response.json()
bookSet(response)
}
if (!book) return false //first render, when useEffect did't triggered yet we will return false
return <div>{JSON.stringify(book)}</div>
}
Run Code Online (Sandbox Code Playgroud)
Jka*_*nen 19
如果您正在构建自定义钩子,有时会导致无限循环,默认情况如下
function useMyBadHook(values = {}) {
useEffect(()=> {
/* This runs every render, if values is undefined */
},
[values]
)
}
Run Code Online (Sandbox Code Playgroud)
解决方法是使用相同的对象,而不是在每次函数调用时都创建一个新对象:
const defaultValues = {};
function useMyBadHook(values = defaultValues) {
useEffect(()=> {
/* This runs on first call and when values change */
},
[values]
)
}
Run Code Online (Sandbox Code Playgroud)
如果您在组件代码中遇到此问题,如果您使用 defaultProps 而不是 ES6 默认值,则循环可能会得到修复
function MyComponent({values}) {
useEffect(()=> {
/* do stuff*/
},[values]
)
return null; /* stuff */
}
MyComponent.defaultProps = {
values = {}
}
Run Code Online (Sandbox Code Playgroud)
bes*_*rtm 11
如果在useEffect末尾包含空数组:
useEffect(()=>{
setText(text);
},[])
Run Code Online (Sandbox Code Playgroud)
它会运行一次。
如果您还包括数组上的参数:
useEffect(()=>{
setText(text);
},[text])
Run Code Online (Sandbox Code Playgroud)
只要文本参数改变,它就会运行。
Roc*_*que 10
如文档(https://reactjs.org/docs/hooks-effect.html)中所述,当您希望在每次渲染后执行一些代码时useEffect,可以使用该钩子。从文档:
useEffect是否在每次渲染后运行?是!
如果您要对此进行自定义,则可以按照同一页面稍后出现的说明进行操作(https://reactjs.org/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects)。基本上,该useEffect方法接受第二个参数,React将检查该参数以确定是否必须再次触发效果。
useEffect(() => {
document.title = `You clicked ${count} times`;
}, [count]); // Only re-run the effect if count changes
Run Code Online (Sandbox Code Playgroud)
您可以将任何对象作为第二个参数传递。如果此对象保持不变,则只会在第一次安装后触发效果。如果对象改变,效果将再次被触发。
Ben*_*arp 10
你的无限循环是由于循环
useEffect(() => {
setIngredients({});
}, [ingredients]);
Run Code Online (Sandbox Code Playgroud)
setIngredients({});将更改ingredients(每次都会返回一个新引用)的值,这将运行setIngredients({}). 要解决此问题,您可以使用任一方法:
const timeToChangeIngrediants = .....
useEffect(() => {
setIngredients({});
}, [timeToChangeIngrediants ]);
Run Code Online (Sandbox Code Playgroud)
setIngrediants将在timeToChangeIngrediants更改时运行。
Object.values(ingrediants)可以将第二个参数作为 useEffect 传递。useEffect(() => {
setIngredients({});
}, Object.values(ingrediants));
Run Code Online (Sandbox Code Playgroud)
小智 9
我不确定这是否适合你,但你可以尝试添加 .length 像这样:
useEffect(() => {
// fetch from server and set as obj
}, [obj.length]);
Run Code Online (Sandbox Code Playgroud)
就我而言(我正在获取一个数组!)它在装载时获取数据,然后再次仅在更改时获取数据,并且没有进入循环。
我也遇到了同样的问题,并通过确保在第二个参数中传递原始值来解决了该问题 []。
如果您传递一个对象,React将仅存储对该对象的引用,并在引用更改时运行效果,该更改通常是每个辛格时间(我现在不知道如何)。
解决方案是在对象中传递值。你可以试试,
const obj = { keyA: 'a', keyB: 'b' }
useEffect(() => {
// do something
}, [Object.values(obj)]);
Run Code Online (Sandbox Code Playgroud)
要么
const obj = { keyA: 'a', keyB: 'b' }
useEffect(() => {
// do something
}, [obj.keyA, obj.keyB]);
Run Code Online (Sandbox Code Playgroud)
当将复杂对象作为状态并从以下位置更新时,我经常遇到无限重新渲染useRef:
const [ingredients, setIngredients] = useState({});
useEffect(() => {
setIngredients({
...ingredients,
newIngedient: { ... }
});
}, [ingredients]);
Run Code Online (Sandbox Code Playgroud)
在这种情况下,eslint(react-hooks/exhaustive-deps)迫使我(正确地)添加ingredients到依赖项数组中。然而,这会导致无限的重新渲染。与此线程中的某些人所说的不同,这是正确的,并且您无法将ingredients.someKey或ingredients.length放入依赖项数组中。
解决方案是 setter 提供您可以参考的旧值。您应该使用它,而不是直接引用ingredients:
const [ingredients, setIngredients] = useState({});
useEffect(() => {
setIngredients(oldIngedients => {
return {
...oldIngedients,
newIngedient: { ... }
}
});
}, []);
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
20900 次 |
| 最近记录: |