componentWillUnmount에서 가져 오기를 취소하는 방법
제목이 다 나와 있다고 생각합니다. 여전히 가져 오는 구성 요소를 마운트 해제 할 때마다 노란색 경고가 표시됩니다.
콘솔경고 : 마운트 해제 된 구성 요소 에서는 호출
setState
(또는forceUpdate
) 할 수 없습니다 . 이것은 작동하지 않지만 ... 수정하려면componentWillUnmount
메서드의 모든 구독 및 비동기 작업을 취소하십시오 .
constructor(props){
super(props);
this.state = {
isLoading: true,
dataSource: [{
name: 'loading...',
id: 'loading',
}]
}
}
componentDidMount(){
return fetch('LINK HERE')
.then((response) => response.json())
.then((responseJson) => {
this.setState({
isLoading: false,
dataSource: responseJson,
}, function(){
});
})
.catch((error) =>{
console.error(error);
});
}
Promise를 실행하면 해결되기까지 몇 초가 걸릴 수 있으며 그 때까지 사용자가 앱의 다른 위치로 이동했을 수 있습니다. 따라서 Promise resolves setState
가 마운트되지 않은 구성 요소에서 실행되고 귀하의 경우와 마찬가지로 오류가 발생합니다. 이로 인해 메모리 누수가 발생할 수도 있습니다.
그렇기 때문에 일부 비동기 논리를 구성 요소에서 이동하는 것이 가장 좋습니다.
그렇지 않으면 어떻게 든 약속을 취소해야합니다 . 또는 최후의 수단 (반 패턴)으로 구성 요소가 여전히 마운트되어 있는지 확인하기 위해 변수를 유지할 수 있습니다.
componentDidMount(){
this.mounted = true;
this.props.fetchData().then((response) => {
if(this.mounted) {
this.setState({ data: response })
}
})
}
componentWillUnmount(){
this.mounted = false;
}
다시 한 번 강조하겠습니다. 이것은 반 패턴 이지만 귀하의 경우에는 충분할 수 있습니다 ( Formik
구현 과 마찬가지로 ).
GitHub 에 대한 유사한 토론
편집하다:
이것은 아마도 내가 Hooks로 같은 문제를 어떻게 해결할 것인가 (React 만 남았음) 일 것이다 .
옵션 A :
import React, { useState, useEffect } from "react";
export default function Page() {
const value = usePromise("https://something.com/api/");
return (
<p>{value ? value : "fetching data..."}</p>
);
}
function usePromise(url) {
const [value, setState] = useState(null);
useEffect(() => {
let isMounted = true; // track whether component is mounted
request.get(url)
.then(result => {
if (isMounted) {
setState(result);
}
});
return () => {
// clean up
isMounted = false;
};
}, []); // only on "didMount"
return value;
}
옵션 B : 또는 useRef
클래스의 정적 속성처럼 작동하여 값이 변경 될 때 구성 요소를 다시 렌더링하지 않습니다.
function usePromise2(url) {
const isMounted = React.useRef(true)
const [value, setState] = useState(null);
useEffect(() => {
return () => {
isMounted.current = false;
};
}, []);
useEffect(() => {
request.get(url)
.then(result => {
if (isMounted.current) {
setState(result);
}
});
}, []);
return value;
}
// or extract it to custom hook:
function useIsMounted() {
const isMounted = React.useRef(true)
useEffect(() => {
return () => {
isMounted.current = false;
};
}, []);
return isMounted; // returning "isMounted.current" wouldn't work because we would return unmutable primitive
}
예 : https://codesandbox.io/s/86n1wq2z8
React의 친절한 사람들은 취소 가능한 약속으로 가져 오기 호출 / 약속을 포장 할 것을 권장 합니다. 해당 문서에는 페치로 코드를 클래스 또는 함수와 별도로 유지하라는 권장 사항이 없지만 다른 클래스 및 함수에이 기능이 필요할 가능성이 높고 코드 복제는 안티 패턴이며 느린 코드에 관계없이 코드를 유지하는 것이 좋습니다. 에서 폐기하거나 취소해야합니다 componentWillUnmount()
. React 에 따라 마운트되지 않은 구성 요소에 대한 상태 설정을 피하기 위해 cancel()
래핑 된 promise를 호출 할 수 있습니다 componentWillUnmount
.
제공된 코드는 React를 가이드로 사용하는 경우 다음 코드 스 니펫과 유사합니다.
const makeCancelable = (promise) => {
let hasCanceled_ = false;
const wrappedPromise = new Promise((resolve, reject) => {
promise.then(
val => hasCanceled_ ? reject({isCanceled: true}) : resolve(val),
error => hasCanceled_ ? reject({isCanceled: true}) : reject(error)
);
});
return {
promise: wrappedPromise,
cancel() {
hasCanceled_ = true;
},
};
};
const cancelablePromise = makeCancelable(fetch('LINK HERE'));
constructor(props){
super(props);
this.state = {
isLoading: true,
dataSource: [{
name: 'loading...',
id: 'loading',
}]
}
}
componentDidMount(){
cancelablePromise.
.then((response) => response.json())
.then((responseJson) => {
this.setState({
isLoading: false,
dataSource: responseJson,
}, () => {
});
})
.catch((error) =>{
console.error(error);
});
}
componentWillUnmount() {
cancelablePromise.cancel();
}
---- 편집하다 ----
GitHub의 문제에 따라 주어진 답변이 정확하지 않을 수 있음을 발견했습니다. 다음은 내 목적에 맞는 하나의 버전입니다.
export const makeCancelableFunction = (fn) => {
let hasCanceled = false;
return {
promise: (val) => new Promise((resolve, reject) => {
if (hasCanceled) {
fn = null;
} else {
fn(val);
resolve(val);
}
}),
cancel() {
hasCanceled = true;
}
};
};
아이디어는 가비지 수집기가 함수를 만들거나 null을 사용하여 메모리를 확보하도록 돕는 것입니다.
AbortController 를 사용 하여 가져 오기 요청을 취소 할 수 있습니다 .
class FetchComponent extends React.Component{
state = { todos: [] };
controller = new AbortController();
componentDidMount(){
fetch('https://jsonplaceholder.typicode.com/todos',{
signal: this.controller.signal
})
.then(res => res.json())
.then(todos => this.setState({ todos }))
.catch(e => alert(e.message));
}
componentWillUnmount(){
this.controller.abort();
}
render(){
return null;
}
}
class App extends React.Component{
state = { fetch: true };
componentDidMount(){
this.setState({ fetch: false });
}
render(){
return this.state.fetch && <FetchComponent/>
}
}
ReactDOM.render(<App/>, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
게시물이 열렸 기 때문에 "abortable-fetch"가 추가되었습니다. https://developers.google.com/web/updates/2017/09/abortable-fetch
(문서에서 :)
컨트롤러 + 신호 기동 AbortController와 AbortSignal을 만나보세요 :
const controller = new AbortController();
const signal = controller.signal;
컨트롤러에는 한 가지 방법 만 있습니다.
controller.abort (); 이렇게하면 다음과 같은 신호를 알립니다.
signal.addEventListener('abort', () => {
// Logs true:
console.log(signal.aborted);
});
이 API는 DOM 표준에 의해 제공되며 전체 API입니다. 의도적으로 일반적이므로 다른 웹 표준 및 JavaScript 라이브러리에서 사용할 수 있습니다.
예를 들어, 다음은 5 초 후에 가져 오기 시간 초과를 만드는 방법입니다.
const controller = new AbortController();
const signal = controller.signal;
setTimeout(() => controller.abort(), 5000);
fetch(url, { signal }).then(response => {
return response.text();
}).then(text => {
console.log(text);
});
"모든 구독을 취소하고 비 동기화"해야 할 때 일반적으로 componentWillUnmount의 redux에 무언가를 발송하여 다른 모든 구독자에게 알리고 필요한 경우 취소에 대한 요청을 하나 더 서버에 보냅니다.
이 경고의 핵심은 구성 요소에 일부 미해결 콜백 / 약속에 의해 보유 된 참조가 있다는 것입니다.
두 번째 패턴에서와 같이 isMounted 상태를 유지하는 (컴포넌트를 활성 상태로 유지하는) 반 패턴을 피하기 위해 react 웹 사이트는 선택적 promise를 사용할 것을 제안 합니다 . 그러나 그 코드는 또한 객체를 살아있게 유지하는 것처럼 보입니다.
대신 setState에 중첩 된 바인딩 함수가있는 클로저를 사용하여 수행했습니다.
여기 내 생성자 (typescript)가 있습니다…
constructor(props: any, context?: any) {
super(props, context);
let cancellable = {
// it's important that this is one level down, so we can drop the
// reference to the entire object by setting it to undefined.
setState: this.setState.bind(this)
};
this.componentDidMount = async () => {
let result = await fetch(…);
// ideally we'd like optional chaining
// cancellable.setState?.({ url: result || '' });
cancellable.setState && cancellable.setState({ url: result || '' });
}
this.componentWillUnmount = () => {
cancellable.setState = undefined; // drop all references.
}
}
I think I figured a way around it. The problem is not as much the fetching itself but the setState after the component is dismissed. So the solution was to set this.state.isMounted
as false
and then on componentWillMount
change it to true, and in componentWillUnmount
set to false again. Then just if(this.state.isMounted)
the setState inside the fetch. Like so:
constructor(props){
super(props);
this.state = {
isMounted: false,
isLoading: true,
dataSource: [{
name: 'loading...',
id: 'loading',
}]
}
}
componentDidMount(){
this.setState({
isMounted: true,
})
return fetch('LINK HERE')
.then((response) => response.json())
.then((responseJson) => {
if(this.state.isMounted){
this.setState({
isLoading: false,
dataSource: responseJson,
}, function(){
});
}
})
.catch((error) =>{
console.error(error);
});
}
componentWillUnmount() {
this.setState({
isMounted: false,
})
}
i use axios
to make a request to my api:
componentDidMount() is invoked immediately after a component is mounted (inserted into the tree)
componentWillUnmount() is invoked immediately before a component is unmounted and destroyed
export default class index extends Component{
_isMounted = false;
state = {
produtos : undefined
}
componentDidMount(){
this._isMounted=true;
}
componentWillUnmount(){
this._isMounted = false;
}
onclick(){
async function get(){
await api.post(`https://yourapi/Products/Select`,{cod_Produto:cod_produto})
.then(function(response){
if (this._isMounted===true){
this.setState({produtos:undefined});
});
}
get();
}
Font: 1 - https://www.robinwieruch.de/react-warning-cant-call-setstate-on-an-unmounted-component .
Font: 2 - https://reactjs.org/docs/react-component.html .
axios:https://github.com/axios/axios
참고URL : https://stackoverflow.com/questions/49906437/how-to-cancel-a-fetch-on-componentwillunmount
'code' 카테고리의 다른 글
C ++ 11 이니셜 라이저 목록이 실패하지만 길이가 2 인 목록에서만 (0) | 2020.10.30 |
---|---|
Gradle 프로젝트의 Maven 아티팩트 ID를 어떻게 설정합니까? (0) | 2020.10.30 |
log4net으로 추적 메시지를 기록하는 방법은 무엇입니까? (0) | 2020.10.30 |
Android에서 내 애플리케이션 이름 지정 (0) | 2020.10.30 |
timeIntervalSinceDate는 어떤 시간 단위를 반환합니까? (0) | 2020.10.30 |