? 快速上手三大基礎 React Hooks

JS菌發表於2019-03-13

Hooks 出了有段時間了,不知盆友們有在專案中開始使用了嗎❓如果還沒了解的童鞋,可以瞧瞧這篇文章,對比看下三大基礎 Hooks 和傳統 class 元件的區別和用法吧?

我們所指的三個基礎 Hooks 是:

  • useState 在函式式元件內維護 state
  • useEffect 函式式元件內有副作用的呼叫與 componentDidMountcomponentDidUpdate 類似但又有所區別
  • useContext 監聽 provider 更新變化

useState

useState 允許我們在函式式元件中維護 state,傳統的做法需要使用類元件。舉個例子?,我們需要一個輸入框,隨著輸入框內容的改變,元件內部的 label 標籤顯示的內容也同時改變。下面是兩種不同的寫法:

不使用 useState

import React from "react";
// 1
export class ClassTest extends React.Component {
  // 2
  state = {
    username: this.props.initialState
  }
  // 3
  changeUserName(val) {
    this.setState({ username: val })
  }
  // 4
  render() {
    return (
      <div>
        <label style={{ display: 'block' }} htmlFor="username">username: {this.state.username}</label>
        <input type="text" name="username" onChange={e => this.changeUserName(e.target.value)} />
      </div>
    )
  }
}
複製程式碼
  1. 我們需要定義一個類並從 React.Component 處繼承
  2. 還需要初始化一個 state
  3. 初始化改變 state 的方法
  4. 最後還要使用 render 函式返回 JSX

✅使用 useState

// 1
import React, { useState } from "react";

export function UseStateTest({ initialState }) {
  // 2
  let [username, changeUserName] = useState(initialState)
  // 3
  return (
    <div>
      <label style={{ display: 'block' }} htmlFor="username">username: {username}</label>
      <input type="text" name="username" onChange={e => changeUserName(e.target.value)} />
    </div>
  )
}
複製程式碼

在父元件中使用:

import React from "react";
// 引入元件
import { UseStateTest } from './components/UseStateTest'

// 4
const App = () => (
  <div>
    <UseStateTest initialState={'initial value'} />
  </div>
)

export default App;
複製程式碼
  1. 需要從 react 中引入 useState 這個?
  2. 使用 useState 方法,接收一個初始化引數,定義 state 變數,以及改變 state 的方法
  3. 在需要使用的地方直接使用 state 這個變數即可,增加事件處理函式觸發改變 state 的方法
  4. 在父元件中呼叫,通過 props 傳遞 initialState 初始化值

useState 方法替換掉原有的 class 不僅效能會有所提升,而且可以看到程式碼量減少很多,並且不再需要使用 this,所以能夠維護 state 的函式式元件真的很好用?

  • class classTest extends React.Components {} ? function UseStateTest(props) {} 函式式元件寫法
  • this.state.username ? username 使用 state 不需要 this
  • this.setState({ username: '' }) ? changeUserName('') 改變 state 也不需要書寫 setState 方法

文件說明:https://zh-hans.reactjs.org/docs/hooks-state.html

useEffect

useEffect 是專門用來處理副作用的,獲取資料、建立訂閱、手動更改 DOM 等這都是副作用。你可以想象它是 componentDidMountcomponentDidUpdatecomponentWillUnmount 的結合。

舉個例子??,比方說我們建立一個 div 標籤,每當點選就會傳送 http 請求並將頁面 title 改為對應的數值:

import React from 'react'
// 1
import { useState, useEffect } from 'react'

export function UseEffectTest() {
    let [msg, changeMsg] = useState('loading...')
    // 2
    async function getData(url) { // 獲取 json 資料
        return await fetch(url).then(d => d.json())
    }
    // 3
    async function handleClick() { // 點選事件改變 state
        let data = await getData('https://httpbin.org/uuid').then(d => d.uuid)
        changeMsg(data)
    }
    // 4
    useEffect(() => { // 副作用
        document.title = msg
    })
    return (
        <div onClick={() => handleClick()}>{msg}</div>
    )
}
複製程式碼
  1. 首先從 react 中引入 useEffect ?
  2. 然後建立獲取資料的 getData 方法
  3. 建立事件處理函式 handleClick
  4. 使用 useEffect 處理副作用:改變頁面的 title

如果使用傳統的類元件的寫法:

import React from 'react'
// 1
export class ClassTest extends React.Component {
    // 2
    state = {
        msg: 'loading...'
    }
    // 3
    async getData(url) { // 獲取 json 資料
        return await fetch(url).then(d => d.json())
    }
    handleClick = async () => { // 點選事件改變 state
        let data = await this.getData('https://httpbin.org/uuid').then(d => d.uuid)
        this.setState({ msg: data })
    }
    // 4
    componentDidMount() {
        document.title = this.state.msg
    }
    componentDidUpdate() {
        document.title = this.state.msg
    }
    // 5
    render() {
        return (
            <div onClick={this.handleClick}>{this.state.msg}</div>
        )
    }
}
複製程式碼
  1. 首先建立類 ClassTest
  2. 初始化 state
  3. 定義獲取資料方法和事件處理函式
  4. componentDidMountcomponentDidUpdate 階段改變 document.title
  5. 最後通過 render 函式渲染

這一堆東西寫完人都睡著了?

使用 useEffect 不僅去掉了部分不必要的東西,而且合併了 componentDidMountcomponentDidUpdate 方法,其中的程式碼只需要寫一遍。?

第一次渲染和每次更新之後都會觸發這個鉤子,如果需要手動修改自定義觸發規則

見文件:https://zh-hans.reactjs.org/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects

另外,官網還給了一個訂閱清除訂閱的例子:

20190313113615.png

使用 useEffect 直接 return 一個函式即可:

20190313113627.png

返回的函式是選填的,可以使用也可以不使用:

20190313113753.png

文件:https://zh-hans.reactjs.org/docs/hooks-effect.html#recap

比方說我們使用 useEffect 來解綁事件處理函式:

useEffect(() => {
    window.addEventListener('keydown', handleKeydown);
    return () => {
        window.removeEventListener('keydown', handleKeydown);
    }
})
複製程式碼

useContext

useContext 的最大的改變是可以在使用 Consumer 的時候不必在包裹 Children 了,比方說我們先建立一個上下文,這個上下文裡頭有一個名為 usernamestate,以及一個修改 username 的方法 handleChangeUsername

建立上下文

不使用 useState:

不使用 state hooks 的程式碼如下:

import React, { createContext } from 'react'

// 1. 使用 createContext 建立上下文
export const UserContext = new createContext()

// 2. 建立 Provider
export class UserProvider extends React.Component {

    handleChangeUsername = (val) => {
        this.setState({ username: val })
    }

    state = {
        username: '',
        handleChangeUsername: this.handleChangeUsername
    }

    render() {
        return (
            <UserContext.Provider value={this.state}>
                {this.props.children}
            </UserContext.Provider>
        ) 
    }
}

// 3. 建立 Consumer
export const UserConsumer = UserContext.Consumer
複製程式碼

看看我們做了什麼:

  1. 首先我們使用 createContext 建立了上下文
  2. 然後我們建立 Provider;裡頭定義了 handleChangeUsername 方法和 usernamestate,並返回一個包裹 this.props.childrenProvider
  3. 最後我們再返回 UserContext.Consumer

程式碼比較冗長,可以使用上文提到的 useState 對其進行精簡:

✅使用 useState:

使用 state hooks

import React, { createContext, useState } from 'react'

// 1. 使用 createContext 建立上下文
export const UserContext = new createContext()

// 2. 建立 Provider
export const UserProvider = props => {
    let [username, handleChangeUsername] = useState('')
    return (
        <UserContext.Provider value={{username, handleChangeUsername}}>
            {props.children}
        </UserContext.Provider>
    )
}

// 3. 建立 Consumer
export const UserConsumer = UserContext.Consumer
複製程式碼

使用 useState 建立上下文更加簡練。

使用上下文

上下文定義完畢後,我們再來看使用 useContext 和不使用 useContext 的區別是啥?:

不使用 useContext:

import React  from "react";
import { UserConsumer, UserProvider } from './UserContext'

const Pannel = () => (
  <UserConsumer>
    {/* 不使用 useContext 需要呼叫 Consumer 包裹 children */}
    {({ username, handleChangeUsername }) => (
      <div>
        <div>user: {username}</div>
        <input onChange={e => handleChangeUsername(e.target.value)} />
      </div>
    )}
  </UserConsumer>
)

const Form = () => <Pannel></Pannel>

const App = () => (
  <div>
    <UserProvider>
      <Form></Form>
    </UserProvider>
  </div>
)

export default App;
複製程式碼

✅使用 useContext:

只需要引入 UserContext,使用 useContext 方法即可:

import React, { useContext }  from "react"; // 1
import { UserProvider, UserContext } from './UserContext' // 2

const Pannel = () => {
  const { username, handleChangeUsername } = useContext(UserContext) // 3
  return (
    <div>
      <div>user: {username}</div>
      <input onChange={e => handleChangeUsername(e.target.value)} />
    </div>
  )
}

const Form = () => <Pannel></Pannel>

// 4
const App = () => (
  <div>
    <UserProvider>
      <Form></Form>
    </UserProvider>
  </div>
)

export default App;
複製程式碼

看看做了啥:

  1. 首先第一步引入useContext
  2. 然後引入 UserProvider 以及上下文 UserContext
  3. 再需要使用的元件中呼叫 useContext 方法獲取 state
  4. 當然前提是要在父元件中使用 UserProvider 巢狀主 children

這樣通過 useContextuseState 就重構完畢了,看起來程式碼又少了不少?

以上,三個基礎的 Hooks 入門就講解完畢了,上手就是這樣,函式式元件和 Hooks 配合使用真的非常爽⛄

參考:

  • https://codeburst.io/quick-intro-to-react-hooks-6dd8ecb898ed
  • https://reactjs.org/docs/hooks-reference.html

? 快速上手三大基礎 React Hooks

相關文章