函數化的React Hooks

寫在前面

在2018年React Conf大會上,React團隊講解了目前你們使用React開發過程存在的一些問題並推出了一個使人激動的概念:React Hooks,並介紹hooks如何解決這些問題。恰好我所在的團隊前端使用的是react+ts+node這套技術棧,前段時間將react版本升級到了16.8,在這個過程當中使用到了hooks的功能。在這裏寫下本身的感覺和看法。前端

本文將從如下幾部分進行總結:node

  1. React Hooks以前還存在哪些痛點?
  2. React Hooks帶來的代碼模式改變
  3. 總結
  4. 擴展閱讀

React Hooks以前還存在哪些痛點?

1.組件間類似的邏輯複用難

在react hooks問世以前,react能提供給組件間複用邏輯採用的是mixin,但這種方式弊端很是多,因此你們基本都是用render props(渲染屬性)和HOC(高階組件)來複用組件間的邏輯。但這兩種方式的缺點也是很是明顯的,很容易產生「wrapper hell」,即組件很容易臃腫,增長了debugger的成本。react

2.生命週期的問題

在稍微複雜的業務邏輯當中,生命週期是繞不過去的問題。有不少場景下在componentDidmount處理的邏輯不得不又在componentDidUpdate從新處理一次,業務被分散在各個不一樣的生命週期函數中。 編程

而Hooks的出現,從面向生命週期編程轉變爲面向業務邏輯編程 數組

3.對人以及機器都不友好的class

雖然Hooks以前能夠編寫純函數組件,但它只能是無狀態的。很是常見的狀況是你編寫了一個純函數組件,但過一段時間你發現得加上一個狀態,這時候不得不將純函數組件改成類組件。這時候得處理this的問題,並且編譯器對class也是不友好的。bash

React Hooks帶來的代碼模式改變

import { useState, useEffect } from 'react' 
function Example(props) {
  // 聲明一個新的狀態變量"count" 
  const [count, setCount] = useState(0);

  useEffect(() => {
    subscribe(props.number, setCount)
    return () => {
      unsubscribe(props.number)
    }
  })

  return <div>{count}</div>
}
複製代碼

useState會接受一個初始值,而後會返回一個[狀態,狀態修改器]的二元組。每次從新渲染時,整個函數會從新執行,可是useState會記住上次的值。並且react是根據useState的調用順序來記得狀態歸屬的:app

const Example = () => {
    const [size, setSize] = useState({ width: 100, height: 100 });
    const [count, setCount] = useState(0);   
}
複製代碼

每一次 Example 被渲染,都是第一次 useState 調用得到 size 和 setSize,第二次 useState 調用得到 count 和 setCount。函數

useEffect是用來處理反作用的,至關於之前的componentDidMount和componentDidUpdate。它能夠返回一個函數,用來在組件卸載前調用。因此useEffect聚合了componentDidMount,componentDidUpdate和componentWillUnmount的操做。若是props.number沒有改變,咱們是不但願在useEffect訂閱/取消訂閱裏從新執行的,爲了實現這個操做,只須要傳入一個參數便可。ui

useEffect(() => {
    subscribe(props.number, setCount)
    return () => {
      unsubscribe(props.number)
    }
  }, [props.number])
複製代碼

它是一個數組,只要這個數組中的每個值都不發生變化,則useEffect不須要從新執行。this

在將react升級以後,生命週期函數遷移到函數組件能夠按照如下方式來操做:

  • constructor: 使用 useState 來初始化 state。
  • getDerivedStateFromProps: 函數組件自己能夠直接操做。
  • shouldComponentUpdate: 使用 React.memo。
  • render: 函數組件自己。
  • componentDidMount, componentDidUpdate, componentWillUnmount: 使用 useEffect 實現。

在react hooks開發的過程當中,共享代碼會採起函數組件的形式。約定使用useXXX開頭,想重用這些代碼,在函數式組件經過調用useXXX便可。

例如咱們能夠寫這樣一個useMountLog,在組件mount的時候打印出一條日誌:

const useMountLog = (name) => {
    useEffect(() => {
        console.log(`${name} mounted`);    
    });
}
複製代碼

這樣一來,全部的函數式組件中均可以經過調用useMountLog來使用這個功能:

const Example = () => {
    useMountLog('Example')
}
複製代碼

再好比能夠經過寫useWindowWidth這樣一個自定義hook來反映當前窗口的寬度

function useWindowWidth() {
  const [width, setWidth] = useState(window.innerWidth);
  
  useEffect(() => {
    const handleResize = () => setWidth(window.innerWidth);
    window.addEventListener('resize', handleResize);
    return () => {
      window.removeEventListener('resize', handleResize);
    };
  });
  
  return width;
}
複製代碼

這樣就能夠在其餘函數式組件中使用:

function MyResponsiveComponent() {
  const width = useWindowWidth();
  return (
    <p>Window width is {width}</p>
  );
}
複製代碼

在上邊的例子能夠看到,react hooks大大地減小咱們的代碼量。而hooks只能在函數式組件中使用,因此能夠預測雖然如今react仍然支持類組件,但之後類組件會慢慢得消亡。

總結

React Hooks的出現,將大大地減小react的代碼量,解決原來使用react開發遇到的一些問題,也給開發帶來了一些變化。包括:

  • react hooks將改變組件間重用代碼的方式。使用自定義 hooks,沒有 mixin 帶來的混亂,沒有 HOC 帶來的層級深淵。
  • 從面向生命週期編程到面向業務邏輯編程
  • 再也不須要class,再也不須要關注this等問題

最後,引用 Dan Abramov 在React Conf 2018 上的演講詞結束本文:

「……我曾經疑惑,React 的 Logo 爲何是一個原子?後來我想到了這個解釋。咱們知道物質由原子組成,是原子的特性決定了物質的外觀和行爲。就像 React,你能夠把用戶視圖拆成獨立的組件,再像原子同樣自由組合起來,是組件的特性決定了用戶視圖的行爲。科學家們曾一度認爲原子是不可分割的最小單位,直到發現了電子,一種原子內部更小的粒子。事實上,是電子的特徵影響了原子的性質。我認爲 hooks 就比如電子,與其說它是一個新特性,不如說是已知的 React 特性(state,context,生命週期)的更直接的展示形式,而這四年來咱們卻一直對它視而不見。

若是盯着 React 的 logo 看的話,你會發現 hooks 其實一直都在。」

擴展閱讀

Making Sense of React Hooks

React Today and Tomorrow and 90% Cleaner React

對React Hooks的一些思考

相關文章
相關標籤/搜索