# 1. 组件臃肿
# 2. 直接更改state
# 3. props该传数字类型的值却传了字符串,反之亦然
# 4. list组件中没使用key
# 5. setState是异步操作
# 6. 频繁使用Redux
# 7. 组件没以大写字母开头命名
# 8. 数据返回之前组件被销毁
React 会报一个 Warning
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 http://function.in Notification
# 解决办法:
一、标记一下组件有没有被卸载,可以利用 useEffect 的返回值
import React, { useState, useEffect } from 'react';
import Axios from 'axios';
const useAjax = (initurl) => {
let [data, setdata] = useState('')
let [isloadding, setisload] = useState(false)
useEffect(() => {
let isUnmounted = true;
(async () => {
setisload(true)
let res = await Axios.get(initurl)
if (isUnmounted) {
setdata(res.data.msg)
setisload(false)
}
})()
return () => {
isUnmounted = false;
}
}, [])
return [data, isloadding]
}
const Index = () => {
let [data, isloadding] = useAjax('http://rap2.taobao.org:38080/app/mock/248242/api/text?key=react')
return (
<div>
{
isloadding ? <h1>loadding...</h1> : <h1>{data}</h1>
}
</div>
);
};
export default Index;
二、更加优雅的解法 【这个方法暂时不建议使用】
上述做法是在收到响应时进行判断,即无论如何需要等响应完成,略显被动。
一个更加主动的方式是探知到卸载时直接中断请求,自然也不必再等待响应了。
这种主动方案需要用到 AbortController。
AbortController 是一个浏览器的实验接口,它可以返回一个信号量(singal),从而中止发送的请求。
这个接口的兼容性不错,除了 IE 之外全都兼容(如 Chrome, Edge, FF 和绝大部分移动浏览器,包括 Safari)。
useEffect(() => {
let isUnmounted = false;
const abortController = new AbortController(); // 创建
(async () => {
const res = await fetch(SOME_API, {
singal: abortController.singal, // 当做信号量传入
});
const data = await res.json();
if (!isUnmounted) {
setValue(data.value);
setLoading(false);
}
})();
return () => {
isUnmounted = true;
abortController.abort(); // 在组件卸载时中断
}
}, []);
singal 的实现依赖于实际发送请求使用的方法,如上述例子的 fetch 方法接受 singal 属性。 如果使用的是 axios,它的内部已经包含了 axios.CancelToken,可以直接使用
# 代码实例(第一种方法的实例)
import React, { useEffect, useState, } from 'react';
import Axios from 'axios';
//自定义的hooks
const useAjax = (initurl) => {
let [state, setState] = useState('')
let [loadding, setloadding] = useState(false)//加载中的状态
useEffect(() => {
setloadding(true)
const getdata = async () => {
let res = await Axios.get(initurl)
setTimeout(() => { //这里使用了一个定时器,是为了让它在多少秒后执行
setState(res.status)
}, 2000);
setloadding(false)
}
getdata()
}, [state])
return [state, loadding]
}
//子组件1
const Child1 = () => {
let [state, loadding] = useAjax('http://api.tianapi.com/txapi/ncovcity/index?key=964dc226dd5b57e892e6199735b6c55f')
return <div>
{loadding ? <h1>loadding....</h1> : <h1>{state}</h1>}
</div>
}
//子组件2
const Child2 = () => {
return <h1>我是chind2</h1>
}
//父组件
const Index = () => {
let [sem, setem] = useState(false)
return (
<div>
{
sem ? <Child1 /> : <Child2 />
}
<button onClick={() => setem(!sem)}>切换</button>
</div>
);
};
export default Index;
通过连续切换组件你会发现,还没有完成修改状态时,组件就被销毁了,然而销毁的组件就会继续执行,控制台这时就会报错
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 http://function.in Notification
....省略和以上相同的代码
//自定义的hooks
const useAjax = (initurl) => {
let [state, setState] = useState('')
let [loadding, setloadding] = useState(false)
useEffect(() => {
let bool = false //做一个标记
setloadding(true)
const getdata = async () => {
let res = await Axios.get(initurl)
if (!bool) { //进行判断
setTimeout(() => {
setState(res.status)
}, 2000);
setloadding(false)
}
}
getdata()
return () => { //在useEffect中return 一个函数,相当于生命周期里的组件销毁阶段
bool = true
}
}, [state])
return [state, loadding]
}
....省略和以上相同的代码