React--hooks--useEffect的使用

前言:

在react的class组件中,我们通过生命周期的钩子函数来完成一些副作用操作(发送网络请求,手动变更 DOM,记录日志等)。那么切换到函数组件,没有了this指向的生命周期钩子函数我们又要如何处理这些副作用操作呢?
React-hooks 为我们提供了 useEffect 钩子函数,来让我更好的处理副作用。
我们可以把 useEffect 看做 componentDidMountcomponentDidUpdatecomponentWillUnmount 这三个函数的组合。


一、如何使用useEffect

1.什么是副作用操作 ?

在 React 组件中有两种常见副作用操作:需要清除的不需要清除的

副作用:在组件中做的事,已经不算组件的核心逻辑了,那它就是副作用:发送网络请求,手动变更 DOM,记录日志等…

2. 在class中处理副作用(不需要清除的)

// 需求:组件第一次加载或更新后 手动操作更新 document.title 
class App extends React.Component{
    constructor(props){
        super(props)
        this.state = {
            count:0
        }
    }
    render(){
        return (
            <div>
                <p>You Clicked:{this.state.count}</p>
                <button onClick={()=>this.setState({count:this.state.count + 1})}>Click</button>
            </div>
        ) 
    }
    // 在两个生命周期钩子函数中,我们需要在两个生命周期函数中编写重复的代码。
    componentDidMount(){
        console.log(document.title); // React App
        document.title = `Click ${this.state.count} times`
        console.log(document.title); // Click 0 times
    }
    componentDidUpdate(){
        console.log("componentDidUpdate");
        document.title = `Click ${this.state.count} times`
    }
}

3.在函数组件中使用useEffect处理副作用(不需要清除的)

function App() {
    const [state, setState] = useState({
        count: 0
    })
    // useEffect直接声明
    // 每次渲染都会执行
    // 直接定义在函数组件中,可以直接访问state和props
    // 其内部函数的运行,不会阻塞浏览器视图的更新
    useEffect(()=>{
        document.title = `Click ${state.count} times`
        console.log(document.title);
    })
    return (
        <div>
            <p>You Clicked:{state.count}</p>
            <button onClick={() => setState({ count: state.count + 1 })}>Click</button>
        </div>
    )
}

4.为什么要使用useEffect ?

在函数组件主体内(这里指在 React 渲染阶段)改变 DOM、添加订阅、设置定时器、记录日志以及执行其他包含副作用的操作都是不被允许的,因为这可能会产生莫名其妙的 bug 并破坏 UI 的一致性。

二、useEffect的参数

useEffect可以传入两个参数:

1.第一个参数是 函数内部装着处理副作用的逻辑

2.第二个参数是 依赖项数组
(1)如果 不传 第二个参数,则 useEffect 每一次渲染都会执行
(2)如果 传入一个空数组 ,则 useEffect 只会执行一次
(3)如果 传入的数组中有依赖项,则依赖项的值改变了,useEffect 才会执行

function App() {
    const [state, setState] = useState({
        count: 0
    })
    // 传入空数组,useEffect只会执行一次
    useEffect(()=>{
        document.title = `Click ${state.count} times`
        console.log(document.title); // 执行一次后就不会更新
    }, [])
    return (
        <div>
            <p>You Clicked:{state.count}</p>
            <button onClick={() => setState({ count: state.count + 1 })}>Click</button>
        </div>
    )
}
function App() {
    const [state, setState] = useState({
        count: 0
    })
    const [name,setName] = useState('')
    const [age,setAge] = useState(18)
    
    // 添加依赖项后,只有依赖项数组内的值变化了才会触发useEffect
    // 现在只有state变化,useEffect才会执行,name和age变化不会触发useEffect
    useEffect(()=>{
        console.log("useEffect");
        document.title = `Click ${state.count} times`
        console.log(document.title);
    },[state])
    return (
        <div>
            <p>You Clicked:{state.count}</p>
            <p>You Name:{name}</p>
            <p>You Age:{age}</p>
            <button onClick={() => setState({ count: state.count + 1 })}>Click</button>
            <button onClick={() => setName(`name ${state.count}`)}>Click Name</button>
            <button onClick={() => setAge(age + 1)}>Click Age</button>
        </div>
    )
}

三、处理需要清除的副作用

没有清除副作用时:

function App(){
    const [width,setWidth] = useState(document.body.clientWidth)
    
    const resizeWidth = ()=>{
        setWidth(document.body.clientWidth)
    }
    const removeComponent = useCallback(()=>{
        ReactDOM.unmountComponentAtNode(document.getElementById('root'))
    },[])
    useEffect(()=>{
        window.addEventListener('resize',resizeWidth,false)
    })
    return (
        <div>
            <p>body client width:{width}</p>
            <button onClick={removeComponent}>Remove Component</button>
        </div>
    )
}

在这里插入图片描述
点击移除之后在这里插入图片描述
由此可见,移除之后,addEventListener仍然在监听width的变化,系统资源没有得到释放,所以我们要清除副作用

消除副作用我们可以在传入的函数中,再return一个回调函数消除副作用

function App(){
    const [width,setWidth] = useState(document.body.clientWidth)
    
    const resizeWidth = ()=>{
        setWidth(document.body.clientWidth)
        console.log(width);
    }
    const removeComponent = useCallback(()=>{
        ReactDOM.unmountComponentAtNode(document.getElementById('root'))
    },[])
    useEffect(()=>{
        window.addEventListener('resize',resizeWidth,false)
        // 在return中返回一个回调函数,清除监听器,消除副作用
        return ()=>{
            console.log("removeEventListener");
            window.removeEventListener('resize',resizeWidth,false)
        }
    })
    return (
        <div>
            <p>body client width:{width}</p>
            <button onClick={removeComponent}>Remove Component</button>
        </div>
    )
}

消除副作用之后,就不会再触发监听事件了

为什么要在 effect 中返回一个函数?

返回一个函数可以让我们消除副作用

这是 effect 可选的清除机制。每个 effect 都可以返回一个清除函数。如此可以将添加和移除订阅的逻辑放在一起。
这就是为什么 React 会在执行当前 effect 之前对上一个 effect 进行清除。这样资源得到释放,提高了项目的性能。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值