React-Native useEffect cleanup



我正在尝试从API获取数据-它通常有效,但有时我会Warning: 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.

我的useEffect

useEffect(() => {
let abortController = new AbortController();
getAccountInformation(abortController);
return () => { 
abortController.abort();
};
}, [isFocused]);

功能

async function getAccountInformation(abortController) {
try {
const token = await AsyncStorage.getItem("token");
const client_id = await AsyncStorage.getItem("client_id");
await fetch(API_ADD + "/getClientInformation/" + client_id, {
signal: abortController.signal,
method: "GET",
headers: {
Authorization: "Bearer " + token,
"Content-Type": "application/json",
},
})
.then((response) => response.text())
.then((responseJson) => {
const safeResponse = responseJson.length
? JSON.parse(responseJson)
: {};
setClientInformation(safeResponse);
getBookingsByClientId(abortController);
}).catch(err=>{
if(err.name==='AbortError')
{
console.log("Fetch abort - caught an error");
}
else 
{
console.log(err.message);
Alert.alert(err.message);
}
})
.done();
} catch (e) {
console.log(e);
}
}

不幸的是,我找不到任何解决方案-只有useEffect 内的函数的解决方案

AbortController只能拒绝第一个'fetch'承诺。更改状态前只需检查signal.aborted即可。
async function getAccountInformation(abortController) {
try {
const token = await AsyncStorage.getItem("token");
const client_id = await AsyncStorage.getItem("client_id");
const {signal}= abortController;
await fetch(API_ADD + "/getClientInformation/" + client_id, {
signal,
method: "GET",
headers: {
Authorization: "Bearer " + token,
"Content-Type": "application/json",
},
})
.then((response) => response.text())
.then((responseJson) => {
const safeResponse = responseJson.length
? JSON.parse(responseJson)
: {};
if(!signal.aborted){ // do not change the state if controller has been aborted
setClientInformation(safeResponse);
getBookingsByClientId(abortController);
}
}).catch(err=>{
if(err.name==='AbortError')
{
console.log("Fetch abort - caught an error");
}
else 
{
console.log(err.message);
Alert.alert(err.message);
}
})
.done();
} catch (e) {
console.log(e);
}
}

理想情况下,您应该在卸载组件后尽快中断该功能,以避免做不必要的工作。但在一个最小的解决方案中,您只需要避免状态更改。

或者你可以查看这个带有自定义钩子使用的演示,它可以处理取消&请求在卸载时自动中止(或者如果用户请求(:

import React, { useState } from "react";
import {
useAsyncCallback,
CanceledError,
E_REASON_UNMOUNTED
} from "use-async-effect2";
import cpFetch from "cp-fetch";
export default function TestComponent(props) {
const [text, setText] = useState("");
const fetchCharacter = useAsyncCallback(
function* (id) {
const response = yield cpFetch(
`https://rickandmortyapi.com/api/character/${id}`
);
return yield response.json();
}
);
const fetchUrl = useAsyncCallback(
function* () {
this.timeout(props.timeout);
try {
setText("fetching...");
const response = yield cpFetch(props.url);
const json = yield response.json();
const character = yield fetchCharacter(Math.round(Math.random() * 100));
setText(
JSON.stringify(
{
firstResponse: json,
character
},
null,
2
)
);
} catch (err) {
CanceledError.rethrow(err, E_REASON_UNMOUNTED);
setText(err.toString());
}
},
[props.url, props.timeout]
);
return (
<div className="component">
<div className="caption">useAsyncEffect demo:</div>
<div>{text}</div>
<button className="btn btn-success" onClick={() => fetchUrl(props.url)}>
Fetch data
</button>
<button className="btn btn-warning" onClick={() => fetchUrl.cancel()}>
Cancel request
</button>
</div>
);
}

最新更新