1. 组件臃肿

# 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]
}
....省略和以上相同的代码