自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

useEffect 實(shí)踐案例:自定義 Hook

開(kāi)發(fā) 前端
我們常常會(huì)封裝一個(gè)函數(shù)用于邏輯的復(fù)用。自定義 Hook 也是這樣的一個(gè)在 React 組件內(nèi)部用于邏輯復(fù)用的函數(shù)封裝。

我們將在上一章案例的基礎(chǔ)之上學(xué)習(xí)自定義 hook。

上一章中,我們巧妙的把大量的 JSX 邏輯處理封裝在了 List 組件中,使得在頁(yè)面組件的代碼變得非常簡(jiǎn)單。這是針對(duì) UI 層的邏輯處理,那么在數(shù)據(jù)的處理上,是否也能夠進(jìn)行一些封裝呢?

// 數(shù)據(jù)的主要核心邏輯
const str = useRef('')
const [list, setList] = useState<string[]>([])
const [error, setError] = useState('')
const [loading, setLoading] = useState(true)

function getList() {
  searchApi(str.current).then(res => {
    setList(res)
    setLoading(false)
    setError('')
  }).catch(err => {
    setLoading(false)
    setError(err)
  })
}

useEffect(() => {
  loading && getList()
}, [loading])

function onSure() {
  setLoading(true)
}

答案是肯定的,解決方案就是我們將要在本章中學(xué)習(xí)的自定義 hook。

一、自定義hook

我們常常會(huì)封裝一個(gè)函數(shù)用于邏輯的復(fù)用。自定義 hook 也是這樣的一個(gè)在 react 組件內(nèi)部用于邏輯復(fù)用的函數(shù)封裝。

和普通函數(shù)封裝相比,他唯一的特殊之處就在于我們常常會(huì)將 react 內(nèi)置 hook 封裝在邏輯之中,比如 useState,useEffect 等。除此之外,為了區(qū)分與普通的函數(shù)封裝,我們必須以 use 開(kāi)頭為自定義 hook 命名,這樣的 hook 只能在 React 組件中使用。

以上一章中的數(shù)據(jù)處理邏輯為例,我們來(lái)封裝一個(gè)自定義 hook,將其命名為 useFetch。

function useFetch() {}

我們先考慮單個(gè)場(chǎng)景的封裝,單純只是為了讓組件看上去更簡(jiǎn)潔。

我們就可以把所有的數(shù)據(jù)和處理數(shù)據(jù)的邏輯封裝起來(lái)。

import {useEffect, useState, useRef} from 'react'
import { searchApi } from './api'

export default function useFetch() {
  const str = useRef('')
  const [list, setList] = useState<string[]>([])
  const [error, setError] = useState('')
  const [loading, setLoading] = useState(true)

  function getList() {
    searchApi(str.current).then(res => {
      setList(res)
      setLoading(false)
      setError('')
    }).catch(err => {
      setLoading(false)
      setError(err)
    })
  }

  useEffect(() => {
    loading && getList()
  }, [loading])

  return { str, list, error, loading, setLoading }
}

封裝過(guò)程非常簡(jiǎn)單,就是把之前那一堆邏輯全部遷移過(guò)來(lái),最后返回應(yīng)用組件里需要的數(shù)據(jù)和方法即可。

return { str, list, error, loading, setLoading }

OK,此時(shí)我們來(lái)觀察一下組件里的代碼。

export default function DemoOneNormal() {
  const {loading, setLoading, str, list, error} = useFetch()  

  return (
    <Block className={s.container} title={td.title} desc={td.desc}>
      <div className={r.flex}>
        <input
          className={s.input}
          placeholder="請(qǐng)輸入您要搜索的內(nèi)容"
          onChange={(e) => str.current = e.target.value}
        />
        <Button
          className={s.button}
          onClick={() => setLoading(true)}
        >
          搜索
        </Button>
      </div>
      <List
        list={list}
        loading={loading}
        error={error}
        renderItem={(item) => (
          <div key={item} className={s.item}>{item}</div>
        )}
      />
    </Block>
  )
}

邏輯簡(jiǎn)潔了許多。變成了簡(jiǎn)單的同步代碼:通過(guò)一個(gè)方法獲取數(shù)據(jù),并將數(shù)據(jù)渲染到 UI 組件。

Block 組件是單獨(dú)封裝的布局組件,希望不要因此造成任何理解上的困難。

一個(gè)組件變成了數(shù)據(jù)與UI的結(jié)合。我們分別將復(fù)雜的數(shù)據(jù)處理邏輯封裝在 hook 里,將復(fù)雜的UI交互邏輯封裝在基礎(chǔ) UI 組件里,在使用時(shí),利用他們的封裝結(jié)果進(jìn)行組合,能夠簡(jiǎn)單,高效的組合出復(fù)雜的頁(yè)面,這也是我們?cè)趯?shí)踐中最大的追求

這里有些人可能會(huì)有一些疑問(wèn),我只是把一些邏輯放在了另外的地方,代碼量最終不僅沒(méi)有減少,反而還變多了,這樣做的好處真的有那么大嗎?當(dāng)然,因?yàn)槲覀兎庋b的 useFetch 和 List 組件,他們承載了大多數(shù)的復(fù)雜邏輯,并且只會(huì)在最開(kāi)始的時(shí)候編寫一次,在以后的使用中,就直接引入使用就行了,這極大的簡(jiǎn)化了后續(xù)的開(kāi)發(fā)工作量,對(duì)工作效率的提高非常顯著

二、進(jìn)一步思考

此時(shí)的封裝雖然足夠簡(jiǎn)潔。但是沒(méi)有考慮復(fù)用。因此還需要進(jìn)一步思考改進(jìn)。

我們來(lái)分析一下場(chǎng)景:每一個(gè)需要信息展示的頁(yè)面,基本邏輯都是在初始化時(shí),請(qǐng)求接口,獲得數(shù)據(jù),然后展示信息。我們可以把不同情況的接口請(qǐng)求抽象成為一個(gè)接口,然后基于這個(gè)場(chǎng)景來(lái)思考不同頁(yè)面的請(qǐng)求的共性與差異。

每個(gè)頁(yè)面都要處理信息展示、異常等邏輯,差異的地方就在于獲取數(shù)據(jù)的 api 函數(shù)不一樣,他返回的數(shù)據(jù)內(nèi)容,數(shù)據(jù)類型也不一樣。

不一樣的東西作為參數(shù)傳入,那我們只需要將 api 函數(shù)作為參數(shù)傳入即可。

const info = useFetch(searchApi)

不過(guò)我們此時(shí)還需要考慮的是,為了確保自定義 hook 的返回類型具備完整準(zhǔn)確的類型推導(dǎo),我們還需要約定傳入 api 的參數(shù)類型與返回類型。

因此,在定義 useFetch 時(shí),我們先用 ts 約定 api 的具體類型,因?yàn)閰?shù)類型和返回值類型在封裝時(shí)都不確定,只能在具體的實(shí)參傳入之后才能明確,因此使用兩個(gè)泛型來(lái)分別表示參數(shù)類型和返回值類型。

type API<T, P> 
  = (param?: P) => Promise<T>

正常代碼不會(huì)這樣換行,之所以這樣只是為了在移動(dòng)端能夠更多的展示代碼信息而不用滾動(dòng)查看。

然后在定義 useFetch 時(shí)傳入這兩個(gè)泛型即可,完整代碼如下:

import { useEffect, useState, useRef } from 'react'

type API<T, P> = (param?: P) => Promise<T>

export default function useFetch<T, P>(api: API<T, P>) {
  const param = useRef<P>()
  const [list, setList] = useState<T>()
  const [error, setError] = useState('')
  const [loading, setLoading] = useState(true)

  function getList() {
    api(param.current).then(res => {
      setList(res)
      setLoading(false)
      setError('')
    }).catch(err => {
      setLoading(false)
      setError(err)
    })
  }

  useEffect(() => {
    loading && getList()
  }, [loading])

  return { 
    param, 
    setParam: (p: P) => param.current = p,
    list, 
    error, 
    loading, 
    setLoading 
  }
}

因?yàn)樵谑褂脮r(shí),傳入的 api 函數(shù)已經(jīng)具備了完善的類型,因此我們這種寫法可以借助 ts 內(nèi)部的自動(dòng)推導(dǎo)而簡(jiǎn)化使用時(shí)在 ts 上的繁瑣。

const {
  loading, 
  setLoading, 
  setParam,
  list,
  error
} = useFetch(searchApi)

雖然在使用層面沒(méi)有任何 ts 的痕跡,但是返回值的類型已經(jīng)非常明確。

由于在封裝過(guò)程中我們沒(méi)有處理默認(rèn)值的情況,因此返回類型可能為 undefined,這在實(shí)踐中一定要引起重視。你可以根據(jù)實(shí)際情況往 useFetch 傳入默認(rèn)值,也可以在使用層面初始化默認(rèn)值

const {
  loading, 
  setLoading, 
  setParam,
  list = [],
  error
} = useFetch(searchApi)

這樣,一個(gè)通用,高效,且具備準(zhǔn)確類型提示的 hook 就被我們封裝好了。

在實(shí)踐過(guò)程中,由于不同的團(tuán)隊(duì)有不同的需求,你還需要根據(jù)自己的需求和項(xiàng)目實(shí)際情況做相應(yīng)的細(xì)節(jié)調(diào)整,切記不要完整套用。

三、取舍

由于面試的影響,讓不少前端同行錯(cuò)誤的把性能當(dāng)成了實(shí)踐中最重要的標(biāo)準(zhǔn)。但其實(shí)工作中性能并不是最高的優(yōu)先級(jí)。我們往往會(huì)在可接受的范圍之內(nèi),犧牲性能換取其他的便利。

例如,多一層函數(shù)封裝,其實(shí)也就意味著執(zhí)行壓力多那么一點(diǎn)點(diǎn)。但是他可能換來(lái)的是開(kāi)發(fā)效率的極大提高。

因此,在我們的課程案例決策當(dāng)中,提供的方案并不會(huì)把性能當(dāng)做第一準(zhǔn)則,代碼的可讀性、可維護(hù)性、開(kāi)發(fā)效率的優(yōu)先級(jí)都會(huì)比性能更高。只要我們?cè)趯懘a的過(guò)程中,非常明確的知道這種方式我們舍棄了什么,得到了什么,你權(quán)衡之后,愿意做出這樣的取舍,那么這樣的方式就是可以使用的。

當(dāng)然,性能依然非常重要,如果你的頁(yè)面出現(xiàn)了卡頓,我們就應(yīng)該思考一下,是不是對(duì)性能的犧牲有點(diǎn)過(guò)了頭。

責(zé)任編輯:姜華 來(lái)源: 這波能反殺
相關(guān)推薦

2023-11-30 07:45:11

useEffectReact

2017-05-19 10:03:31

AndroidBaseAdapter實(shí)踐

2022-06-06 09:28:36

ReactHook

2017-05-18 12:36:16

android萬(wàn)能適配器列表視圖

2025-01-22 11:10:34

2023-09-27 22:10:47

Vue.jsJavaScript

2021-02-23 08:01:01

HooksReact架構(gòu)

2010-08-12 09:45:33

jQuery自定義事件

2023-06-28 08:05:46

場(chǎng)景vue3自定義

2015-02-12 15:33:43

微信SDK

2015-02-12 15:38:26

微信SDK

2023-06-27 15:02:47

2016-12-26 15:25:59

Android自定義View

2024-06-13 09:50:45

2016-02-26 14:57:50

飛象網(wǎng)

2011-12-16 14:23:51

Java

2015-01-14 15:06:48

定義相機(jī)

2009-06-08 20:13:36

Eclipse自定義控

2022-04-24 15:17:56

鴻蒙操作系統(tǒng)

2021-11-23 15:06:42

Kubernetes 運(yùn)維開(kāi)源
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)