리액트 훅마운트 해제된 구성 요소에서 반응 상태 업데이트를 수행할 수 없음
다음의 에러가 표시됩니다.
마운트 해제된 구성 요소에서 반응 상태 업데이트를 수행할 수 없습니다.이것은 no-op이지만, 애플리케이션의 메모리 누수를 나타내고 있습니다.수정하려면 useEffect 정리 함수의 모든 구독 및 비동기 작업을 취소합니다.
데이터 가져오기가 시작되고 구성 요소가 마운트 해제되었지만 함수가 마운트 해제된 구성 요소의 상태를 업데이트하려고 하는 경우.
이 문제를 해결하는 가장 좋은 방법은 무엇입니까?
CodePen의 예.
default function Test() {
const [notSeenAmount, setNotSeenAmount] = useState(false)
useEffect(() => {
let timer = setInterval(updateNotSeenAmount, 2000)
return () => clearInterval(timer)
}, [])
async function updateNotSeenAmount() {
let data // here i fetch data
setNotSeenAmount(data) // here is problem. If component was unmounted, i get error.
}
async function anotherFunction() {
updateNotSeenAmount() //it can trigger update too
}
return <button onClick={updateNotSeenAmount}>Push me</button> //update can be triggered manually
}
가장 쉬운 해결책은 구성 요소의 마운트 여부를 추적하는 로컬 변수를 사용하는 것입니다.이것은 클래스 베이스 어프로치의 일반적인 패턴입니다.다음으로 후크를 사용하여 실장하는 예를 나타냅니다.
function Example() {
const [text, setText] = React.useState("waiting...");
React.useEffect(() => {
let isCancelled = false;
simulateSlowNetworkRequest().then(() => {
if (!isCancelled) {
setText("done!");
}
});
return () => {
isCancelled = true;
};
}, []);
return <h2>{text}</h2>;
}
다음은 (아래 참조)의 대체 방법입니다.종속성 목록을 사용하면 이 솔루션이 작동하지 않습니다.참조 값은 첫 번째 렌더링 후에도 그대로 유지됩니다.이 경우 첫 번째 솔루션이 더 적절합니다.
function Example() {
const isCancelled = React.useRef(false);
const [text, setText] = React.useState("waiting...");
React.useEffect(() => {
fetch();
return () => {
isCancelled.current = true;
};
}, []);
function fetch() {
simulateSlowNetworkRequest().then(() => {
if (!isCancelled.current) {
setText("done!");
}
});
}
return <h2>{text}</h2>;
}
이 패턴에 대한 자세한 내용은 이 문서를 참조하십시오.이 솔루션을 보여주는 GitHub의 React 프로젝트 내부의 문제가 여기에 있습니다.
(훅을 사용하여) Axios에서 데이터를 가져오고 있는데도 오류가 발생할 경우 조건 내에서 setter를 랩하기만 하면 됩니다.
let isRendered = useRef(false);
useEffect(() => {
isRendered = true;
axios
.get("/sample/api")
.then(res => {
if (isRendered) {
setState(res.data);
}
return null;
})
.catch(err => console.log(err));
return () => {
isRendered = false;
};
}, []);
TL;DR
CodeSandBox의 예를 다음에 나타냅니다.
다른 답변은 물론 유효합니다.제가 생각해낸 해결책을 공유하고 싶었을 뿐이에요.이 후크는 React의 useState와 동일하게 동작하지만 컴포넌트가 마운트된 경우에만 setState가 됩니다.컴포넌트의 isMounted 변수와 혼동할 필요가 없기 때문에 더욱 우아하게 느껴집니다.
설치:
npm install use-state-if-mounted
사용방법:
const [count, setCount] = useStateIfMounted(0);
자세한 내용은 훅의 npm 페이지에서 확인할 수 있습니다.
여기 간단한 해결책이 있습니다.이 경고는 요청이 백그라운드에 있는 동안 일부 가져오기 요청을 수행하고(일부 요청에는 시간이 걸리기 때문에) 해당 화면에서 다시 이동하여 반응할 수 없는 상태를 업데이트하면 발생합니다.여기에 예시 코드가 있습니다. 모든 상태 업데이트 전에 이 줄을 작성하십시오.
if(!isScreenMounted.current) return;
다음은 완전한 예시입니다.
import React , {useRef} from 'react'
import { Text,StatusBar,SafeAreaView,ScrollView, StyleSheet } from 'react-native'
import BASEURL from '../constants/BaseURL';
const SearchScreen = () => {
const isScreenMounted = useRef(true)
useEffect(() => {
return () => isScreenMounted.current = false
},[])
const ConvertFileSubmit = () => {
if(!isScreenMounted.current) return;
setUpLoading(true)
var formdata = new FormData();
var file = {
uri: `file://${route.params.selectedfiles[0].uri}`,
type:`${route.params.selectedfiles[0].minetype}`,
name:`${route.params.selectedfiles[0].displayname}`,
};
formdata.append("file",file);
fetch(`${BASEURL}/UploadFile`, {
method: 'POST',
body: formdata,
redirect: 'manual'
}).then(response => response.json())
.then(result => {
if(!isScreenMounted.current) return;
setUpLoading(false)
}).catch(error => {
console.log('error', error)
});
}
return(
<>
<StatusBar barStyle="dark-content" />
<SafeAreaView>
<ScrollView
contentInsetAdjustmentBehavior="automatic"
style={styles.scrollView}>
<Text>Search Screen</Text>
</ScrollView>
</SafeAreaView>
</>
)
}
export default SearchScreen;
const styles = StyleSheet.create({
scrollView: {
backgroundColor:"red",
},
container:{
flex:1,
justifyContent:"center",
alignItems:"center"
}
})
이 답변은 구체적인 질문과 관련이 없지만 나는 같은 답변을 받았다.Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.리액트 신입사원이 해결책을 찾지 못했기 때문이다.
는 ★★★★에 이었다.useState장착되지 않은 컴포넌트에 있습니다.
state (set state function를 을 알 수 .setIsLoading「 」 「 」 、 「 」 。
const Login = () => {
const [isLoading, setIsLoading] = useState(false);
const handleLogin = () => {
setIsLoading(true);
firebase.auth().then(
functionToUnMountLoginSection();
// the problem is here
setIsLoading(false);
)
}
}
은 전화하는 입니다.setIsLoading컴포넌트가 아직 마운트되어 있는 경우 함수를 호출하여 사용자 로그인을 마운트 해제/처리하기 전에 다음 절차를 수행합니다.
firebase.auth().then(
setIsLoading(false);
functionToUnMountLoginSection();
)
상태 관련 데이터를 useEffect 본문에 추가하여 모든 재렌더링 프로세스를 재실행하지 않도록 합니다.이 방법으로 문제를 해결할 수 있습니다.
useEffect(() => {
let timer = setInterval(updateNotSeenAmount, 2000)
return () => clearInterval(timer)
}, [notSeenAmount])
커스텀 훅 솔루션(ReactJs/NextJs)
'shared'라는 이름의 새 폴더를 만들고 그 폴더에 'hooks', 'utils'라는 이름의 두 폴더를 추가합니다.'common'이라는 새 파일을 추가합니다.Functions.js의 inside utils 폴더에 코드 스니펫을 추가합니다.
export const promisify = (fn) => {
return new Promise((resolve, reject) => {
fn
.then(response => resolve(response))
.catch(error => reject(error));
});
};
후크 폴더에 'fetch-hook.js'라는 새 파일을 추가하고 아래에 코드 스니펫을 추가합니다.
import { useCallback, useEffect, useRef } from "react";
import { promisify } from "../utils/commonFunctions";
export const useFetch = () => {
const isUnmounted = useRef(false);
useEffect(() => {
isUnmounted.current = false;
return () => {
isUnmounted.current = true;
};
}, []);
const call = useCallback((fn, onSuccess, onError = null) => {
promisify(fn).then(response => {
console.group('useFetch Hook response', response);
if (!isUnmounted.current) {
console.log('updating state..');
onSuccess(response.data);
}
else
console.log('aborted state update!');
console.groupEnd();
}).catch(error => {
console.log("useFetch Hook error", error);
if (!isUnmounted.current)
if (onError)
onError(error);
});
}, []);
return { call }
};
폴더 구조
이것으로 커스텀 훅이 준비되었습니다.아래와 같이 컴포넌트에 사용합니다.
const OurComponent = (props) => {
//..
const [subscriptions, setSubscriptions] = useState<any>([]);
//..
const { call } = useFetch();
// example method, change with your own
const getSubscriptions = useCallback(async () => {
call(
payment.companySubscriptions(userId), // example api call, change with your own
(data) => setSubscriptions(data),
);
}, [userId]);
//..
const updateSubscriptions = useCallback(async () => {
setTimeout(async () => {
await getSubscriptions();
}, 5000);// 5 seconds delay
}, [getSubscriptions]);
//..
}
이 구성 요소에서는 'updateSubscriptions' 메서드를 호출합니다.커스텀 훅을 사용한 'getSubscriptions' 메서드가 트리거됩니다.5초 전에 update Subscriptions 메서드를 호출한 후 다른 페이지로 이동하려고 하면 커스텀훅이 상태 갱신을 중단하고 이 질문의 제목에 대한 경고를 방지합니다.
반대쪽을 볼래?
getSubscriptions 메서드를 아래 메서드로 변경
const getSubscriptions = useCallback(async () => {
const response = await payment.companySubscriptions(userId);
setSubscriptions(response);
}, [userId]);
이제 'updateSubscriptions' 메서드를 호출하고 5초 전에 다른 페이지로 이동합니다.
이 커스텀 훅을 사용해 보세요.
import { useEffect, useRef } from 'react';
export const useIsMounted = () => {
const isMounted = useRef(false);
useEffect(() => {
isMounted.current = true;
return () => (isMounted.current = false);
}, []);
return isMounted;
};
function Example() {
const isMounted = useIsMounted();
const [text, setText] = useState();
const safeSetState = useCallback((callback, ...args) => {
if (isMounted.current) {
callback(...args);
}
}, []);
useEffect(() => {
safeSetState(setText, 'Hello')
});
}, []);
return <h2>{text}</h2>;
}
언급URL : https://stackoverflow.com/questions/56442582/react-hooks-cant-perform-a-react-state-update-on-an-unmounted-component
'programing' 카테고리의 다른 글
| npm을 사용하여 TypeScript를 최신 버전으로 업데이트하려면 어떻게 해야 합니까? (0) | 2023.04.04 |
|---|---|
| Java를 사용하여 부분 JSON 응답을 반환하려면 어떻게 해야 합니까? (0) | 2023.04.04 |
| spring-boot 어플리케이션에서 swag-ui를 기동할 수 없습니다. (0) | 2023.04.04 |
| 일반 클래스의 형식 매개 변수에서 새 개체 만들기 (0) | 2023.04.04 |
| 어떻게 반응 js에서 한 페이지에서 다른 페이지로 이동합니까? (0) | 2023.04.04 |


