淺談 React Hooks(一)

sliwey發表於2019-02-27

隨著不久前 React@16.8.0 的釋出,Hooks 也算是正式來到了幕前。個人覺得對React來說,Hooks 可以算是一個里程碑式的功能。本文不會詳細介紹 Hooks,如果對 Hooks 還不是很瞭解的,可以先看下 Introducing Hooks

Hooks 帶來了什麼

簡單來說 Hooks 帶來了可以在 Function Components 使用 state 和其它 React 特性的能力。而這帶來的第一個很直觀的好處就是原來寫的 Function Components 因為需求變動需要增加 state 時,再也不需要重構成 Class Components 了(重構不再是火葬場了)。

個人認為 Hooks 帶來的最大益處就是能夠幫我們更好地組織業務程式碼,提高程式碼複用率。我們通過一個例子來說明,假設有這麼一個需求:「元件需要根據瀏覽器寬度的變化,來顯示不同的內容」。

我們先用 Class Component 來實現:

export default class ResizeClassComponent extends React.Component {
  constructor(props) {
    super(props);

    this.resizeHandler = null;
    this.state = {
      width: window.innerWidth,
    }
  }

  componentDidMount() {
    this.resizeHandler = () => {
      this.setState({
        width: window.innerWidth
      })
    }
    window.addEventListener('resize', this.resizeHandler);
  }

  componentWillUnmount() {
    window.removeEventListener('resize', this.resizeHandler);
  }

  render() {
    const { width } = this.state;
    return (
      <div>width: {width}</div>
    )
  }
}
複製程式碼

目前來看沒什麼問題,但是如果說現在又有一個元件需要實現這個功能,一樣的程式碼再寫一遍?那如果有很多元件都需要這個功能呢?每次都寫一遍肯定不現實,有人會說用render props或者高階元件來處理這個問題,那就讓我們來實現一下:

// render props
class ResizeRenderProps extends React.Component {
  constructor(props) {
    super(props);

    this.resizeHandler = null;
    this.state = {
      width: window.innerWidth,
    }
  }

  componentDidMount() {
    this.resizeHandler = () => {
      this.setState({
        width: window.innerWidth
      })
    }
    window.addEventListener('resize', this.resizeHandler);
  }

  componentWillUnmount() {
    window.removeEventListener('resize', this.resizeHandler);
  }

  render() {
    return this.props.children(this.state.width);
  }
}

export default class ResizeClassComponent extends React.Component {
  render() {
    return (
      <ResizeRenderProps>
        {width => <div>width: {width}</div>}
      </ResizeRenderProps>
    )
  }
}
複製程式碼
// 高階元件
function withResize(WrappedComponent) {
  return class ResizeHoc extends React.Component {
    constructor(props) {
      super(props);

      this.resizeHandler = null;
      this.state = {
        width: window.innerWidth,
      }
    }

    componentDidMount() {
      this.resizeHandler = () => {
        this.setState({
          width: window.innerWidth
        })
      }
      window.addEventListener('resize', this.resizeHandler);
    }

    componentWillUnmount() {
      window.removeEventListener('resize', this.resizeHandler);
    }

    render() {
      return <WrappedComponent width={this.state.width} />;
    }
  }
}

export default withResize(class ResizeClassComponent extends React.Component {
  render() {
    return (
      <div>width: {this.props.width}</div>
    )
  }
})
複製程式碼

雖然實現了功能,但是兩種方式都不可避免地對原來元件做了重構,並且增加了一個層級,如果用的多了就會導致「wrapper hell」,拋開這個不談,render props高階元件 本身自帶的複雜度就不夠友好。

那麼 Hooks 是怎麼解決的呢?

import React, { useState, useEffect } from 'react';

function useWindowWidth() {
  const [width, setWidth] = useState(window.innerWidth);

  useEffect(() => {
    const resizeHandler = () => setWidth(window.innerWidth);
    window.addEventListener('resize', resizeHandler);
    return () => window.removeEventListener('resize', resizeHandler)
  })

  return width;
}

export default function ResizeHooks() {
  const width = useWindowWidth();
  return (
    <div>width: {width}</div>
  )
}
複製程式碼

是不是感覺簡單又清楚?只需要自定義一個 Hook(其實也就是一個普通的函式啦,只不過用了內建的幾個Hook),使用的時候就像呼叫一個普通的函式一樣,沒有複雜的概念需要理解。正如前面所說,Hooks 可以幫助我們組織程式碼,讓相關性的程式碼都在一塊,而不是像在 Class Components 裡散落在各個生命週期函式中,貼個圖方便理解:

淺談 React Hooks(一)

圖片來源:twitter.com/prchdk/stat…

從圖中可以發現邏輯相關的程式碼都被單獨抽成了一個個 Hook,這意味著這部分有狀態的邏輯(stateful logic)可以被複用,而且更容易理解。

稍微解釋下什麼叫「有狀態的邏輯(stateful logic)」,這裡的狀態指的就是 React 的 state,對應的無狀態的邏輯就是普通的函式啦。在 Hooks 之前,如果要複用這種有狀態的邏輯,除了上述的 render props高階元件之外就沒有別的更簡單直接的辦法了。而有了 Hooks 之後,這部分邏輯能夠被單獨抽離出,職責更明確,從而使複用更加簡單,程式碼更加清晰,而且也更便於測試。

嘗試一下

不知道我說的這些有沒有引起你對 Hooks 的興趣呢?其實從 React 官方的態度可以看出是比較傾向於 Hooks 的,或者說是 Function Components,雖然說是不會移除掉 Class Components(要是移除了,估計就炸了吧),但是可以在專案中先小範圍試用下嘛,相信我你會喜歡上它的!

總結

前面寫的有點分散,最後再總結一下 Hooks 帶來的開發體驗上的提升:

  • 想給 Function Components 增加 state 時,無需重構程式碼
  • 相比於render props高階元件,提供了更簡單直觀複用有狀態的邏輯(stateful logic)的方法
  • 程式碼邏輯更加統一,更容易組織程式碼結構
  • 便於測試

有了 Hooks 之後,Function Components 不再是「高階字串模版」啦!?

以上只是我個人對 React Hooks 的一些想法,如有不對之處,歡迎指正~~

原文連結:github.com/sliwey/blog…

擴充套件閱讀:

相關文章