精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

一個新的React概念:Effect Event

開發 前端
在React中,有一個「非常容易」被誤用的API —— UseEffect,今天要介紹的Effect Event就屬于由useEffect衍生出的概念。首先來聊聊Event與Effect。useEffect容易被誤用也是因為這兩個概念很容易混淆。

大家好,我卡頌。

每個框架由于實現原理的區別,都會有些獨特的概念。比如:

  • Vue3由于其響應式的實現原理,衍生出ref、reactive等概念。
  • Svelte重度依賴自身的編譯器,所以衍生出與編譯相關的概念(比如其對label標簽的創新性使用)。

在React中,有一個「非常容易」被誤用的API —— useEffect,今天要介紹的Effect Event就屬于由useEffect衍生出的概念。

被誤用的useEffect

本文一共會涉及三個概念:

  • Event(事件)
  • Effect(副作用)
  • Effect Event(副作用事件)

首先來聊聊Event與Effect。useEffect容易被誤用也是因為這兩個概念很容易混淆。

Event的概念

在下面的代碼中,點擊div會觸發點擊事件,onClick是點擊回調。其中onClick就屬于Event:

function App() {
  const [num , update] = useState(0);
  
  function onClick() {
    update(num + 1);
  }
  
  return (
    <div onClick={onClick}>{num}</div>
  )
}

Event的特點是:「是由某些行為觸發,而不是狀態變化觸發的邏輯」。

比如,在上述代碼中,onClick是由「點擊事件」這一行為觸發的邏輯,num狀態變化不會觸發onClick。

Effect的概念

Effect則與Event相反,他是「由某些狀態變化觸發的,而不是某些行為觸發的邏輯」。

比如,在下述代碼中,當title變化后document.title會更新為title的值:

function Title({title}) {
  useEffect(() => {
    document.title = title;
  }, [title])
  
  // ...
}

上述代碼中useEffect的邏輯就屬于Effect,他是由title變化觸發的。除了useEffect外,下面兩個Hook也屬于Effect:

  • useLayoutEffect(不常用)
  • useInsertionEffect(很不常用)

為什么容易誤用?

現在問題來了:Event與Effect的概念完全不同,為什么會被誤用?

舉個例子,在項目的第一個版本中,我們在useEffect中有個初始化數據的邏輯:

function App() {
  const [data, updateData] = useState(null);

  useEffect(() => {
    fetchData().then(data => {
      // ...一些業務邏輯
      // 更新data
      updateData(data);
    })
  }, []);

  // ...
}

隨著項目發展,你又接到一個需求:提交表單后更新數據。

為了復用之前的邏輯,你新增了options狀態(保存表單數據),并將他作為useEffect的依賴:

function App() {
  const [data, updateData] = useState(null);
  const [options, updateOptions] = useState(null);

  useEffect(() => {
    fetchData(options).then(data => {
      // ...一些業務邏輯
      // 更新data
      updateData(data);
    })
  }, [options]);
  
  
  function onSubmit(opt) {
    updateOptions(opt);
  }

  // ...
}

現在,提交表單后(觸發onSubmit回調)就能復用之前的數據初始化邏輯。

這么做實在是方便,以至于很多同學認為這就是useEffect的用法。但其實這是典型的「useEffect誤用」。

仔細分析我們會發現:「提交表單」顯然是個Event(由提交的行為觸發),Event的邏輯應該寫在事件回調中,而不是useEffect中。正確的寫法應該是這樣:

function App() {
  const [data, updateData] = useState(null);

  useEffect(() => {
    fetchData().then(data => {
      // ...一些業務邏輯
      // 更新data
      updateData(data);
    })
  }, []);
  
  
  function onSubmit(opt) {
    fetchData(opt).then(data => {
      // ...一些業務邏輯
      // 更新data
      updateData(data);
    })
  }

  // ...
}

上述例子邏輯比較簡單,兩種寫法的區別不大。但在實際項目中,隨著項目不斷迭代,可能出現如下代碼:

useEffect(() => {
  fetchData(options).then(data => {
    // ...一些業務邏輯
    // 更新data
    updateData(data);
  })
}, [options, xxx, yyy, zzz]);

屆時,很難清楚fetchData方法會在什么情況下執行,因為:

  1. useEffect的依賴項太多了
  2. 很難完全掌握每個依賴項變化的時機

所以,在React中,我們需要清楚的區分Event與Effect,也就是清楚的區分「一段邏輯是由行為觸發的,還是狀態變化觸發的?」

useEffect的依賴問題

現在,我們已經能清楚的區分Event與Effect,按理說寫項目不會有問題了。但是,由于「Effect的機制問題」,我們還面臨一個新問題。

假設我們有段聊天室代碼,當roomId變化后,要重新連接到新聊天室。在這個場景下,聊天室的斷開/重新連接依賴于roomId狀態的變化,顯然屬于Effect,代碼如下:

function ChatRoom({roomId}) {
  useEffect(() => {
    const connection = createConnection(roomId);
    connection.connect();
    
    return () => {
      connection.disconnect()
    };
  }, [roomId]);
  
  // ...
}

接下來你接到了新需求 —— 當連接成功后,彈出「全局提醒」:

「全局提醒」是否是黑暗模式,受到theme props影響。useEffect修改后的代碼如下:

useEffect(() => {
  const connection = createConnection(roomId);
  connection.connect();
  
  connection.on('connected', () => {
    showNotification('連接成功!', theme);
  });
  
  return () => connection.disconnect();
}, [roomId, theme]);

但這段代碼有個嚴重問題 —— 任何導致theme變化的情況都會導致聊天室斷開/重新連接。畢竟,theme也是useEffect的依賴項。

在這個例子中,雖然Effect依賴theme,但Effect并不是由theme變化而觸發的(他是由roomId變化觸發的)。

為了應對這種場景,React提出了一個新概念 —— Effect Event。他指那些「在Effect內執行,但Effect并不依賴其中狀態的邏輯」,比如上例中的:

() => {
  showNotification('連接成功!', theme);
}

我們可以使用useEffectEvent(這是個試驗性Hook)定義Effect Event:

function ChatRoom({roomId, theme}) {
  const onConnected = useEffectEvent(() => {
    showNotification('連接成功!', theme);
  });

  useEffect(() => {
    const connection = createConnection(roomId);
    connection.connect();
    
    connection.on('connected', () => {
      onConnected();
    });
    
    return () => {
      connection.disconnect()
    };
  }, [roomId]);
  
  // ...
}

在上面代碼中,theme被移到onConnected(他是個Effect Event)中,useEffect雖然使用了theme的最新值,但并不需要將他作為依賴。

useEffectEvent源碼解析

useEffectEvent的實現并不復雜,核心代碼如下:

function updateEvent(callback) {
  const hook = updateWorkInProgressHook();
  // 保存callback的引用
  const ref = hook.memoizedState;
  // 在useEffect執行前更新callback的引用
  useEffectEventImpl({ref, nextImpl: callback});
  
  return function eventFn() {
    if (isInvalidExecutionContextForEventFunction()) {
      throw new Error(
        "A function wrapped in useEffectEvent can't be called during rendering.",
      );
    }
    return ref.impl.apply(undefined, arguments);
  };
}

其中ref變量保存「callback的引用」。對于上述例子中:

const onConnected = useEffectEvent(() => {
  showNotification('連接成功!', theme);
});

ref保存對如下函數的引用:

() => {
  showNotification('連接成功!', theme);
}

useEffectEventImpl方法接受ref和callback的最新值為參數,在useEffect執行前會將ref中保存的callback引用更新為callback的最新值。

所以,當在useEffect中執行onConnected,獲取的就是ref中保存的下述閉包的最新值:

() => {
  showNotification('連接成功!', theme);
}

閉包中的theme自然也是最新值。

useEffectEvent與useEvent

仔細觀察下useEffectEvent的返回值,他包含了兩個限制:

return function eventFn() {
    if (isInvalidExecutionContextForEventFunction()) {
      throw new Error(
        "A function wrapped in useEffectEvent can't be called during rendering.",
      );
    }
    return ref.impl.apply(undefined, arguments);
};

第一個限制比較明顯 —— 下面這行代碼限制useEffectEvent的返回值只能在useEffect回調中執行(否則會報錯):

if (isInvalidExecutionContextForEventFunction()) {
  // ...    
}

另一個限制則比較隱晦 —— 返回值是個全新的引用:

return function eventFn() {
  // ...
};

如果你不太明白「全新的引用」為什么是個限制,考慮下返回一個useCallback返回值:

return useCallback((...args) => {
    const fn = ref.impl;
    return fn(...args);
}, []);

這將會讓useEffectEvent的返回值成為不變的引用,如果再去掉「只能在useEffect回調中執行」的限制,那么useEffectEvent將是加強版的useCallback。

舉個例子,如果破除上述限制,那么對于下面的代碼:

function App({a, b}) {
  const [c, updateC] = useState(0);
  const fn = useCallback(() => a + b + c, [a, b, c])
  
  // ...
}

用useEffectEvent替代useCallback,代碼如下:

const fn = useEffectEvent(() => a + b + c)

相比于useCallback,他有2個優點:

  1. 不用顯式聲明依賴
  2. 即使依賴變了,fn的引用也不會變,簡直是性能優化的最佳選擇

那么React為什么要為useEffectEvent加上限制呢?

實際上,useEffectEvent的前身useEvent就是遵循上述實現,但是由于:

  1. useEvent的定位應該是Effect Event,但實際用途更廣(可以替代useCallback),這不符合他的定位
  2. 當前React Forget(能生成等效于useMemo、useCallback代碼的官方編譯器)并未考慮useEvent,如果增加這個hook,會提高React Forget實現的難度

所以,useEvent并沒有正式進入標準。相反,擁有更多限制的useEffectEvent反而進入了React文檔[1]。

總結

今天我們學到三個概念:

  • Event:由某些行為觸發,而不是狀態變化觸發的邏輯。
  • Effect:由某些狀態變化觸發的,而不是某些行為觸發的邏輯。
  • Effect Event:在Effect內執行,但Effect并不依賴其中狀態的邏輯。

其中Effect Event在React中的具體實現是useEffectEvent。相比于他的前身useEvent,他附加了2條限制:

  1. 只能在Effect內執行。
  2. 始終返回不同的引用。

在我看來,Effect Event的出現完全是由于Hooks實現機制上的復雜性(必須顯式指明依賴)導致的心智負擔。

畢竟,同樣遵循Hooks理念的Vue Composition API就沒有這方面問題。

參考資料

[1]React文檔:https://react.dev/learn/separating-events-from-effects。

責任編輯:姜華 來源: 魔術師卡頌
相關推薦

2022-06-05 21:27:40

Reacteffect

2011-12-14 15:53:51

云計算

2009-10-01 09:19:45

PHP框架ZendFramewoCake

2010-04-01 14:05:41

云計算

2009-10-20 14:10:00

CCIE經驗

2014-07-02 10:03:42

App推廣渠道

2021-06-21 15:49:39

React動效組件

2024-03-19 00:00:00

ReactJavaScript開發

2022-03-16 17:01:35

React18并發的React組件render

2016-08-04 14:59:56

分段路由器路由器

2023-01-30 09:01:34

DecoratorsJS語法

2009-04-20 23:29:12

Oracle收購Sun甲骨文

2024-04-15 12:54:00

ReactVue列表邏輯

2015-08-06 17:15:28

2023-05-15 08:30:35

YjsReact

2022-06-06 09:28:36

ReactHook

2022-10-29 08:55:19

頁面react

2018-10-22 13:53:02

無人零售無人貨架智能

2014-06-20 10:42:27

Windows 8.1

2018-07-30 08:37:02

數據庫Redis數據結構
點贊
收藏

51CTO技術棧公眾號

久久无码专区国产精品s| 视频一区在线免费观看| 国产精品第一页在线观看| 99久久免费精品国产72精品九九 | 四虎地址8848| 2020最新国产精品| 一本到三区不卡视频| 在线观看免费91| 开心激情综合网| 日本欧美在线观看| 久久久久久久999| 日本激情小视频| 亚洲精品一区国产| 欧美性大战久久久久久久| 国产欧美久久久久| 91大神xh98hx在线播放| 成人精品一区二区三区四区 | 99热在线观看免费精品| 久久综合国产| 日韩成人性视频| 小早川怜子一区二区三区| 日韩av中字| 亚洲成人动漫av| 在线观看国产一区| 国产免费av在线| 成人av在线一区二区三区| 成人写真视频福利网| 波多野结衣视频观看| 亚洲日韩视频| 欧美高清自拍一区| 午夜激情福利网| 欧美三级美国一级| 精品无人区太爽高潮在线播放 | 久久女同互慰一区二区三区| 97超碰人人模人人爽人人看| 一区二区三区黄| 鲁大师成人一区二区三区| 久久免费视频网站| 欧美激情国产精品免费| 亚洲精品久久久| 伊是香蕉大人久久| 久久久久久久久久久久| 日韩超碰人人爽人人做人人添| 欧美电影精品一区二区| 四川一级毛毛片| 国产成人久久精品一区二区三区| 欧美视频中文字幕| 在线观看免费黄网站| 亚洲成人看片| 日本韩国一区二区三区视频| 黄色片久久久久| 中文字幕在线官网| 日韩欧美中文第一页| 91九色在线观看视频| 欧产日产国产精品视频| 狠狠色狠色综合曰曰| 国产白丝袜美女久久久久| 2021天堂中文幕一二区在线观| 亚洲一区在线观看视频| 精品视频在线观看一区| 97蜜桃久久| 精品福利一区二区| 男人揉女人奶房视频60分 | 亚洲一区免费视频| 日韩网站在线免费观看| 国产h片在线观看| 欧美视频在线观看免费网址| 国产日韩一区二区在线观看| 欧美va在线观看| 欧美三级电影精品| 午夜激情影院在线观看| 在线一区二区三区视频| 欧美精品一区二区不卡| 亚洲狠狠婷婷综合久久久久图片| 少妇一区二区视频| 色婷婷成人综合| 丰满少妇被猛烈进入一区二区| 欧美日韩综合| 秋霞午夜一区二区| 在线观看国产黄| 国产精品99久久久久久久女警 | 精品视频久久| 久久综合网hezyo| 国产污视频在线观看| 男人的天堂亚洲在线| 国产精品视频永久免费播放| 国产极品久久久| 2023国产精品| 午夜在线视频免费观看| av中文在线资源库| 欧美色视频在线观看| 日本wwww色| 国产91精品对白在线播放| 久久久国产精品亚洲一区| 免费一级a毛片夜夜看| 日日夜夜免费精品| 99se婷婷在线视频观看| 男人的天堂在线视频| 亚洲色图.com| 国产一区二区三区精彩视频| 伊人亚洲精品| 亚洲精品v天堂中文字幕| 国产大屁股喷水视频在线观看| 欧美午夜免费影院| 国产精品久久久久久超碰| 成人久久精品人妻一区二区三区| 国产亚洲一本大道中文在线| 成人免费a级片| 国产原创一区| 精品视频—区二区三区免费| 九九热最新地址| 久久激情一区| 国产伦理一区二区三区| 免费在线看黄网站| 一本大道av伊人久久综合| 国产精品无码自拍| 97精品一区二区| 国产成人一区二区三区电影| 深夜福利视频网站| 国产精品视频线看| 任你操这里只有精品| 精品综合久久88少妇激情| 久久亚洲国产精品| 中文人妻熟女乱又乱精品| 成人av网站免费观看| 天天综合中文字幕| 欧美三区四区| 亚洲色图18p| 免费看日韩毛片| 国产精一品亚洲二区在线视频| 日韩av高清| 亚洲一区站长工具| 亚洲精品久久久久中文字幕欢迎你 | 91视频com| 丁香六月激情婷婷| 136国产福利精品导航网址应用| 日韩中文字幕在线精品| 中文字幕精品无码亚| 国产亚洲va综合人人澡精品| 丰满人妻中伦妇伦精品app| 国产精品chinese在线观看| 九九九久久久久久| 国产黄色av网站| 亚洲精品免费在线观看| 久久久国产精品久久久| 欧美二区视频| 国产精品9999久久久久仙踪林| 91三级在线| 日韩欧美卡一卡二| 久青草免费视频| 成人三级伦理片| aa视频在线播放| 亚洲大片精品免费| 欧洲s码亚洲m码精品一区| 免费在线黄色网址| 在线中文字幕一区二区| 少妇愉情理伦三级| 精品一区二区三区免费毛片爱| 欧美性视频在线播放| 精品一区二区三区亚洲| 色综合天天狠天天透天天伊人| 亚洲av无码乱码国产精品久久| 一区二区不卡在线播放 | 理论片在线不卡免费观看| 国产人妖一区二区三区| 一区二区免费视频| 久久人妻少妇嫩草av无码专区| 99视频一区| 日韩欧美一区二区三区四区| 色诱色偷偷久久综合| 另类天堂视频在线观看| 色呦呦视频在线| 91久久香蕉国产日韩欧美9色| 日韩黄色中文字幕| 国产精品自拍网站| 99在线精品免费视频| 美女少妇全过程你懂的久久 | 亚洲不卡在线| 亚洲91精品在线观看| 久久电影中文字幕| 欧美日韩国产高清一区| 欧美黄色一区二区三区| 91麻豆高清视频| 五月激情五月婷婷| 亚洲茄子视频| 亚洲精品国产精品国自产观看| 亚洲福利影视| 欧美性一区二区三区| 午夜不卡视频| 欧美精品一区视频| 亚洲中文无码av在线| 一区二区免费看| 最近中文字幕免费视频| 国产乱一区二区| 人妻无码视频一区二区三区| 一二三区不卡| 日本精品一区| 白嫩白嫩国产精品| 国产精品视频一区二区三区四 | 色啦啦av综合| 日韩午夜高潮| 最新精品视频| 久久av超碰| 动漫一区二区在线| 99欧美精品| 欧美在线视频网站| 亚洲综合伊人久久大杳蕉| 国产午夜精品免费一区二区三区| 成人av免费播放| 欧美写真视频网站| 亚洲天堂一区在线| 亚洲一区二区黄色| 三上悠亚在线观看视频| 久久综合久久综合久久| 国产精品亚洲一区二区无码| 精品一区二区三区久久久| 日韩欧美精品在线观看视频| 欧美破处大片在线视频| 亚洲国产精品123| 国产成人三级| 蜜桃狠狠色伊人亚洲综合网站| 亚洲精品a区| 91精品久久久久久久久中文字幕| 婷婷六月国产精品久久不卡| 亚洲18私人小影院| free性欧美| 欧美二区乱c黑人| 最近中文字幕免费mv2018在线| 中文字幕av日韩| 国产在线中文字幕| 亚洲欧美中文字幕在线一区| 同心难改在线观看| 亚洲精品国产欧美| 天堂在线视频网站| 精品电影一区二区三区| 国内精品国产成人国产三级| 91精品国产色综合久久不卡电影| 中文字幕777| 欧美午夜精品一区| 亚洲精品无码久久久久| 色94色欧美sute亚洲线路一久| 六月丁香在线视频| 欧美日韩在线一区| 久久视频免费在线观看| 亚洲第一av色| 久久免费激情视频| 色综合中文字幕| 999视频在线| 欧美亚洲国产bt| 亚洲熟妇无码久久精品| 欧美日韩极品在线观看一区| 亚洲性在线观看| 欧美二区在线观看| 国产富婆一级全黄大片| 日韩欧美一级二级| 手机在线观看毛片| 亚洲美女av在线| 97电影在线| 久久精品中文字幕电影| 色黄网站在线观看| 久久青草精品视频免费观看| 女同视频在线观看| 91黑丝高跟在线| 日本精品不卡| 国产原创欧美精品| 日本一区精品视频| 国产亚洲情侣一区二区无| 思热99re视热频这里只精品| 日韩亚洲不卡在线| 一区二区三区毛片免费| 欧美精品一区二区三区三州| 久久久久久久欧美精品| 中文字幕22页| 不卡av电影在线播放| 中文字幕丰满乱子伦无码专区| 国产精品午夜久久| 强行糟蹋人妻hd中文| 欧美日韩亚洲视频一区| 中文在线观看免费高清| 欧美zozo另类异族| 经典三级在线| 久久国产精品久久久久| 天堂av在线网| 91嫩草在线视频| 欧美日日夜夜| 在线亚洲美日韩| 国产精品呻吟| 亚洲制服中文字幕| 91社区在线播放| 成人免费毛片xxx| 精品国产成人av| 国产又黄又粗又硬| 日韩成人在线观看| 成人影院在线观看| 欧洲日本亚洲国产区| 成人网av.com/| 欧美一卡2卡3卡4卡无卡免费观看水多多| 亚洲第一偷拍| 久久精品午夜福利| 国产91精品欧美| 大吊一区二区三区| 欧美日韩亚洲91| 国产成人免费看一级大黄| 亚洲午夜小视频| 国产欧洲在线| 91文字幕巨乱亚洲香蕉| 精品av一区二区| 日本丰满少妇xxxx| 国产一区二区视频在线播放| 受虐m奴xxx在线观看| 亚洲成人av免费| 99久久一区二区| 日韩在线观看视频免费| 黄色亚洲网站| 国产精品久久久久久久小唯西川| 日韩免费视频| 人妻有码中文字幕| 成人黄色国产精品网站大全在线免费观看| 欧美日韩生活片| 色94色欧美sute亚洲13| 亚洲欧美日韩动漫| 国外成人在线直播| 最新国产精品精品视频| 91精品国产毛片武则天| 麻豆国产精品视频| 手机毛片在线观看| 一本到不卡精品视频在线观看 | 在线电影中文日韩| 亚洲黄色免费看| 精品国产乱码久久久久久久软件| 欧美激情91| 97超碰免费在线观看| 亚洲欧美另类图片小说| 亚洲午夜精品久久久| 中文字幕日韩有码| 国产69精品久久| 欧美视频1区| 麻豆精品网站| 免费毛片视频网站| 91久久精品午夜一区二区| 免费在线视频你懂得| 国产99视频精品免视看7| 久久av电影| 亚洲黄色a v| 国产日韩欧美一区二区三区综合| 欧美brazzers| 一区二区三区无码高清视频| 新片速递亚洲合集欧美合集| 日韩欧美手机在线| 麻豆精品视频在线| 波兰性xxxxx极品hd| 91精品国产一区二区人妖| www.久久ai| 成人三级视频在线观看一区二区| 国产精品啊v在线| 国产十八熟妇av成人一区| 午夜精品爽啪视频| 头脑特工队2免费完整版在线观看| 热久久99这里有精品| 国产精品探花在线观看| 欧美伦理视频在线观看| 一区在线观看免费| 国产福利免费视频| 欧美一级高清免费| 日韩精品久久| 青青草精品在线| 午夜电影网亚洲视频| 你懂的在线看| 成人免费福利在线| 在线观看视频日韩| 性欧美丰满熟妇xxxx性仙踪林| 在线观看日韩高清av| av中文字幕在线观看| 国内成+人亚洲| 日韩国产欧美在线观看| 9999热视频| 精品中文字幕久久久久久| 麻豆久久久久| 久久久性生活视频| 国产欧美日韩亚州综合 | 亚洲伦乱视频| 国产资源第一页| 91色在线porny| 91久久精品国产91性色69| 久久久久久美女| 国产一区二区观看| 极品人妻一区二区| 在线免费观看一区| 五月花成人网| 日本一区二区三区在线视频| 国产尤物一区二区在线| 欧美一区二区激情视频| xxxxxxxxx欧美| 婷婷综合福利| 九九九久久久久久久| 色先锋资源久久综合| 黄色网页在线观看| 欧美日韩精品免费看| 国产成人免费在线| 中文字幕一区二区三区四区视频 |