結合React的Effect Hook分析組件副作用的清除


一個訂閱好友在線的組件

我們在DidMount的時候通過ID訂閱了好友的在線狀態,並且為了防止內存泄漏,我們需要在WillUnmount清除訂閱

但是當組件已經顯示在屏幕上時,friend prop 發生變化時會發生什么? 我們的組件將繼續展示原來的好友狀態。這是一個 bug。而且我們還會因為取消訂閱時使用錯誤的好友 ID 導致內存泄露或崩潰的問題。

class FriendStatus extends react.Component { constructor(props) { super(props); this.state = { isOnline: null }; this.handleStatusChange = this.handleStatusChange.bind(this); } componentDidMount() { ChatAPI.subscribeToFriendStatus( this.props.friend.id, this.handleStatusChange ); } componentWillUnmount() { ChatAPI.unsubscribeFromFriendStatus( this.props.friend.id, this.handleStatusChange ); } handleStatusChange(status) { this.setState({ isOnline: status.isOnline }); } render() { if (this.state.isOnline === null) { return 'Loading...'; } return this.state.isOnline ? 'Online' : 'Offline'; } }

 

優化訂閱好友在線的組件

為了解決props更新導致的BUG我們需要在DidUpdate中進行修改訂閱


  componentDidMount() {
    ChatAPI.subscribeToFriendStatus(
      this.props.friend.id, this.handleStatusChange ); } componentDidUpdate(prevProps) { // 取消訂閱之前的 friend.id ChatAPI.unsubscribeFromFriendStatus( prevProps.friend.id, this.handleStatusChange ); // 訂閱新的 friend.id ChatAPI.subscribeToFriendStatus( this.props.friend.id, this.handleStatusChange ); } componentWillUnmount() { ChatAPI.unsubscribeFromFriendStatus( this.props.friend.id, this.handleStatusChange ); }

 

Effect Hook

useEffect() 可以讓你在函數組件中執行副作用操作
默認情況下,它在第一次渲染之后和每次更新之后都會執行。

如果你熟悉 react class 的生命周期函數,你可以把 useEffect Hook 看做 componentDidMount,componentDidUpdate 和 componentWillUnmount 這三個函數的組合。

這是React官網最基礎的Hooks應用

import React, { useState, useEffect } from 'react'; function Example() { const [count, setCount] = useState(0); // Similar to componentDidMount and componentDidUpdate: useEffect(() => { // Update the document title using the browser API document.title = `You clicked ${count} times`; }); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); }

 

需要清除的 effect

為什么要在 effect 中返回一個函數? 這是 effect 可選的清除機制。每個 effect 都可以返回一個清除函數。如此可以將添加和移除訂閱的邏輯放在一起。它們都屬於 effect 的一部分。

React 何時清除 effect? React 會在組件卸載的時候執行清除操作。正如之前學到的,effect 在每次渲染的時候都會執行。這就是為什么 React 會在執行當前 effect 之前對上一個 effect 進行清除。

import React, { useState, useEffect } from 'react'; function FriendStatus(props) { const [isOnline, setIsOnline] = useState(null); useEffect(() => { function handleStatusChange(status) { setIsOnline(status.isOnline); } ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange); // Specify how to clean up after this effect: return function cleanup() { ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange); }; }); if (isOnline === null) { return 'Loading...'; } return isOnline ? 'Online' : 'Offline';

現在假設props.friend.id在更新100->200->300
我們並不需要特定的代碼來處理更新邏輯,因為 useEffect 默認就會處理。它會在調用一個新的 effect 之前對前一個 effect 進行清理。這樣基於不會產生之前因為Props改變而產生的BUG

// Mount with { friend: { id: 100 } } props ChatAPI.subscribeToFriendStatus(100, handleStatusChange); // 運行第一個 effect // Update with { friend: { id: 200 } } props ChatAPI.unsubscribeFromFriendStatus(100, handleStatusChange); // 清除上一個 effect ChatAPI.subscribeToFriendStatus(200, handleStatusChange); // 運行下一個 effect // Update with { friend: { id: 300 } } props ChatAPI.unsubscribeFromFriendStatus(200, handleStatusChange); // 清除上一個 effect ChatAPI.subscribeToFriendStatus(300, handleStatusChange); // 運行下一個 effect // Unmount ChatAPI.unsubscribeFromFriendStatus(300, handleStatusChange); // 清除最后一個 effect

廣州品牌設計公司https://www.houdianzi.com

怎么跳過一些不必要的副作用函數

按照上一節的思路,每次重新渲染都要執行一遍這些副作用函數,顯然是不經濟的。怎么跳過一些不必要的計算呢?我們只需要給useEffect傳第二個參數即可。用第二個參數來告訴react只有當這個參數的值發生改變時,才執行我們傳的副作用函數(第一個參數)。

useEffect(() => { document.title = `You clicked ${count} times`; }, [count]); // 只有當count的值發生變化時,才會重新執行`document.title`這一句

擴展:如果想執行只運行一次的 effect(僅在組件掛載和卸載時執行),可以傳遞一個空數組([])作為第二個參數。這就告訴 React 你的 effect 不依賴於 props 或 state 中的任何值,所以它永遠都不需要重復執行。這並不屬於特殊情況 —— 它依然遵循依賴數組的工作方式。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM