무한 루프 인 useEffect
React 16.7-alpha의 새로운 훅 시스템을 가지고 놀다가 useEffect에서 무한 루프 상태에 빠졌습니다.
먼저 useState를 사용하여 다음과 같은 빈 개체로 시작합니다.
const [obj, setObj] = useState({});
그런 다음 useEffect에서 setObj를 사용하여 다시 빈 객체로 설정합니다.두 번째 인수로 오브젝트의 내용이 변경되지 않으면 갱신되지 않길 바라며 [obj]를 전달합니다.하지만 계속 업데이트 됩니다.내용에 관계없이 이것들은 항상 다른 오브젝트이기 때문에 React는 계속 변화하고 있다고 생각할까요?
useEffect(() => {
setIngredients({});
}, [ingredients]);
어레이도 마찬가지입니다만, 프리미티브로서 예상대로 루프가 되는 일은 없습니다.
이러한 새로운 후크를 사용하여 콘텐츠의 날씨 변경 여부를 확인할 때 객체 및 어레이를 어떻게 처리해야 합니까?
빈 어레이를 useEffect의 두 번째 인수로 전달하면 마운트 및 마운트 해제에서만 실행되므로 무한 루프가 중지됩니다.
useEffect(() => {
setIngredients({});
}, []);
이는 https://www.robinwieruch.de/react-hooks/의 React hooks 블로그 투고에서 명확하게 설명되었습니다.
같은 문제가 있었어.나는 왜 그들이 이것을 의사들에게 언급하지 않는지 모르겠다.Tobias Haugen의 대답에 조금 덧붙이고 싶습니다.
필요한 모든 구성 요소/부모 렌더에서 실행하려면:
useEffect(() => {
// don't know where it can be used :/
})
컴포넌트 마운트 후(한 번 렌더링됨) 어떤 것도 한 번만 실행하려면 다음을 사용해야 합니다.
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
}, [] )
컴포넌트 마운트 및 data/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] )
자주 사용하는 방법:
export default function Book({id}) {
const [book, bookSet] = useState(false)
const loadBookFromServer = useCallback(async () => {
let response = await fetch('api/book/' + id)
response = await response.json()
bookSet(response)
}, [id]) // every time id changed, new book will be loaded
useEffect(() => {
loadBookFromServer()
}, [loadBookFromServer]) // useEffect will run once and when id changes
if (!book) return false //first render, when useEffect did't triggered yet we will return false
return <div>{JSON.stringify(book)}</div>
}
도 한번 두 '아, 아, 아, 아', '아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아,[].
객체를 전달하면 React는 객체에 대한 참조만 저장하고 참조가 변경될 때 효과를 실행합니다(일반적으로 매회).
해결책은 객체의 값을 전달하는 것입니다.시도해 보세요.
const obj = { keyA: 'a', keyB: 'b' }
useEffect(() => {
// do something
}, [Object.values(obj)]);
또는
const obj = { keyA: 'a', keyB: 'b' }
useEffect(() => {
// do something
}, [obj.keyA, obj.keyB]);
커스텀 훅을 구축하는 경우 다음과 같이 디폴트로 무한 루프가 발생할 수 있습니다.
function useMyBadHook(values = {}) {
useEffect(()=> {
/* This runs every render, if values is undefined */
},
[values]
)
}
이 수정은 함수 호출마다 새로운 개체를 작성하는 대신 동일한 개체를 사용하는 것입니다.
const defaultValues = {};
function useMyBadHook(values = defaultValues) {
useEffect(()=> {
/* This runs on first call and when values change */
},
[values]
)
}
컴포넌트 코드에서 이 문제가 발생할 경우 ES6 기본값 대신 defaultProps를 사용하면 루프가 수정될 수 있습니다.
function MyComponent({values}) {
useEffect(()=> {
/* do stuff*/
},[values]
)
return null; /* stuff */
}
MyComponent.defaultProps = {
values = {}
}
당신의 무한 루프는 원형성 때문입니다.
useEffect(() => {
setIngredients({});
}, [ingredients]);
setIngredients({});이 변경됩니다.ingredients됩니다), (실행)(실행).setIngredients({}) 이하려면 ,할 수 있습니다.
- useEffect에 다른 두 번째 인수를 전달합니다.
const timeToChangeIngrediants = .....
useEffect(() => {
setIngredients({});
}, [timeToChangeIngrediants ]);
setIngrediants실행하다timeToChangeIngrediants변
- 일단 성분이 바뀌면 어떤 활용사례가 적절한지 잘 모르겠습니다. 그런이다, 합격입니다.
Object.values(ingrediants)이펙트를 사용하기 위한 두 번째 인수로 사용합니다.
useEffect(() => {
setIngredients({});
}, Object.values(ingrediants));
매뉴얼(https://reactjs.org/docs/hooks-effect.html),에 기재되어 있듯이useEffect훅은 모든 렌더링 후에 일부 코드를 실행할 때 사용됩니다.문서에서:
모든 렌더링 후에 useEffect가 실행됩니까?네!
이것을 커스터마이즈 하려면 , 같은 페이지의 후반부에 표시되는 순서에 따릅니다(https://reactjs.org/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects)).기본적으로는useEffectmethod는 효과가 다시 트리거되어야 하는지 여부를 결정하기 위해 React가 검토하는 두 번째 인수를 받아들입니다.
useEffect(() => {
document.title = `You clicked ${count} times`;
}, [count]); // Only re-run the effect if count changes
두 번째 인수로 임의의 개체를 전달할 수 있습니다.이 개체가 변경되지 않은 상태로 유지되면 첫 번째 마운트 후에만 효과가 트리거됩니다.개체가 변경되면 효과가 다시 트리거됩니다.
이 방법이 도움이 될지는 모르겠지만 다음과 같이 .length를 추가해 보십시오.
useEffect(() => {
// fetch from server and set as obj
}, [obj.length]);
내 경우(어레이를 가져오고 있었는데!) 마운트된 데이터를 가져오고 변경 시에만 데이터를 가져오고 루프 상태가 되지 않았습니다.
useEffect 종료 시 빈 배열을 포함하는 경우:
useEffect(()=>{
setText(text);
},[])
그것은 한 번 작동될 것이다.
배열에 매개 변수도 포함하는 경우:
useEffect(()=>{
setText(text);
},[text])
텍스트 매개 변수가 변경될 때마다 실행됩니다.
할 때 .useRef:
const [ingredients, setIngredients] = useState({});
useEffect(() => {
setIngredients({
...ingredients,
newIngedient: { ... }
});
}, [ingredients]);
경우, 「 」eslint(react-hooks/exhaustive-deps)ingredients이치노그 re-render)가 합니다. 이 글귀는 이 글귀는, ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ᄃ, ᄃ, ᄃ, ᄃ, ᄃ, ᄃ, ᄃ, ᄃ, putting, ᄃ, ᄃ, ingredients.someKey ★★★★★★★★★★★★★★★★★」ingredients.length이치노
해결책은 세터가 참조할 수 있는 오래된 값을 제공하는 것입니다.'이것'을하는 것이 아니라 ''을 사용해야 .ingredients★★★★
const [ingredients, setIngredients] = useState({});
useEffect(() => {
setIngredients(oldIngedients => {
return {
...oldIngedients,
newIngedient: { ... }
}
});
}, []);
이 최적화를 사용하는 경우 시간이 지남에 따라 변경되고 효과에 의해 사용되는 구성 요소 범위(예: 소품 및 상태)의 모든 값이 배열에 포함되어 있는지 확인하십시오.
나는 그들이 오래된 데이터를 사용하고 있다는 가능성을 표현하고 있다는 것을 알고 있다고 생각한다.하지 않습니다.array사용하시는 경우ingredients범위 .array.
const [ingredients, setIngredients] = useState({});
// This will be an infinite loop, because by shallow comparison ingredients !== {}
useEffect(() => {
setIngredients({});
}, [ingredients]);
// If we need to update ingredients then we need to manually confirm
// that it is actually different by deep comparison.
useEffect(() => {
if (is(<similar_object>, ingredients) {
return;
}
setIngredients(<similar_object>);
}, [ingredients]);
주된 문제는 useEffect가 수신 값과 현재 값을 얕게 비교한다는 것입니다.즉, 개체 참조만 확인하는 '===' 비교를 사용하여 이 두 값을 비교하고 배열과 개체 값은 동일하지만 서로 다른 개체로 취급합니다.라이프 사이클 방법으로서 useEffect에 관한 제 기사를 확인해 보시기 바랍니다.
가장 좋은 방법은 Lodash에서 usePrevious() 및 _.isEqual()을 사용하여 이전 값과 현재 값을 비교하는 것입니다.isEqual 및 useRef를 Import합니다.이전 값과 useEffect() 내의 현재 값을 비교합니다.동일한 경우 다른 업데이트를 하지 마십시오.usePrevious(value)는 useRef()를 사용하여 참조를 작성하는 사용자 지정 후크입니다.
아래는 제 코드의 일부입니다.파이어베이스 훅을 사용하여 데이터를 업데이트 할 때 무한 루프 문제에 직면했습니다.
import React, { useState, useEffect, useRef } from 'react'
import 'firebase/database'
import { Redirect } from 'react-router-dom'
import { isEqual } from 'lodash'
import {
useUserStatistics
} from '../../hooks/firebase-hooks'
export function TMDPage({ match, history, location }) {
const usePrevious = value => {
const ref = useRef()
useEffect(() => {
ref.current = value
})
return ref.current
}
const userId = match.params ? match.params.id : ''
const teamId = location.state ? location.state.teamId : ''
const [userStatistics] = useUserStatistics(userId, teamId)
const previousUserStatistics = usePrevious(userStatistics)
useEffect(() => {
if (
!isEqual(userStatistics, previousUserStatistics)
) {
doSomething()
}
})
를 비교할 , 할 때 이 이 오브젝트를 할 수 없습니다.deepCompare비교하기 위해 후크를 끼우다.받아들여진 답변은 확실히 그것을 다루지 않는다.가지고 있다[]어레이는 마운트 시 한 번만 실행할 수 있는 효과가 필요한 경우에 적합합니다.
또한, 다른 투표된 답변은 다음을 수행하여 기본 유형에 대한 확인만 처리합니다.obj.value혹은 처음과 비슷한 것이 네스트되지 않은 수준에 도달합니다.깊이 중첩된 개체에는 이 방법이 적합하지 않을 수 있습니다.
여기 모든 경우에 효과가 있는 것이 있습니다.
import { DependencyList } from "react";
const useDeepCompare = (
value: DependencyList | undefined
): DependencyList | undefined => {
const ref = useRef<DependencyList | undefined>();
if (!isEqual(ref.current, value)) {
ref.current = value;
}
return ref.current;
};
하실 수 있습니다.useEffect을 끼우다
React.useEffect(() => {
setState(state);
}, useDeepCompare([state]));
종속성 배열에서 개체를 제거할 수도 있습니다. 즉, 개체의 특정 부분이 업데이트될 때만 상태가 업데이트됩니다.
예를 들어, 재료에 당근이 들어 있다고 가정해 봅시다.우리는 그것을 의존성에 전달할 수 있습니다.그리고 당근이 바뀌었을 경우에만 상태가 갱신됩니다.
그런 다음 특정 포인트에서 당근 수만 업데이트하면 상태가 업데이트되는 시기를 제어하고 무한 루프를 방지할 수 있습니다.
useEffect(() => {
setIngredients({});
}, [ingredients.carrots]);
이와 같은 기능을 사용할 수 있는 예로는 사용자가 웹 사이트에 로그인하는 경우를 들 수 있습니다.로그인 시 사용자 객체를 구축하여 쿠키와 권한 역할을 추출하고 그에 따라 앱 상태를 업데이트할 수 있습니다.
나의 경우는 무한 루프를 만난 특별한 경우로, 시나리오는 다음과 같습니다.
소품에서 나오는 objX를 소품에서 파괴하는 오브젝트를 가지고 있었습니다.
const { something: { somePropery } } = ObjX
는 '아까운 친구'를 사용했어요.somePropery의 useEffect들면 다음과 같습니다.
useEffect(() => {
// ...
}, [somePropery])
루프를 , 이 모든 시켜서 이 를 해결하려고 .something제대로 작동한 것 같아요
어레이 상태에 사용한 또 다른 작업 솔루션은 다음과 같습니다.
useEffect(() => {
setIngredients(ingredients.length ? ingredients : null);
}, [ingredients]);
언급URL : https://stackoverflow.com/questions/53070970/infinite-loop-in-useeffect
'source' 카테고리의 다른 글
| Rest Assured의 JsonPath를 통해 익명 어레이 요소 액세스 (0) | 2023.02.11 |
|---|---|
| Angular의 http 데이터와 유사한 정적 데이터에서 관찰 가능을 생성하는 방법은 무엇입니까? (0) | 2023.02.11 |
| WooCommerce Retina 이미지 지원 - srcset에는 포함되지 않습니다. (0) | 2023.02.11 |
| 로컬 호스트가 WooCommerce를 업로드 및 설치하기 위한 Wordpress FTP 연결 설정 (0) | 2023.02.11 |
| AngularJS UI-Router는 뷰 내에서 현재 상태를 가져옵니다. (0) | 2023.02.11 |