成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

useEffect 實踐案例:自定義 Hook

開發 前端
我們常常會封裝一個函數用于邏輯的復用。自定義 Hook 也是這樣的一個在 React 組件內部用于邏輯復用的函數封裝。

我們將在上一章案例的基礎之上學習自定義 hook。

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

// 數據的主要核心邏輯
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)
}

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

一、自定義hook

我們常常會封裝一個函數用于邏輯的復用。自定義 hook 也是這樣的一個在 react 組件內部用于邏輯復用的函數封裝。

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

以上一章中的數據處理邏輯為例,我們來封裝一個自定義 hook,將其命名為 useFetch。

function useFetch() {}

我們先考慮單個場景的封裝,單純只是為了讓組件看上去更簡潔。

我們就可以把所有的數據和處理數據的邏輯封裝起來。

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 }
}

封裝過程非常簡單,就是把之前那一堆邏輯全部遷移過來,最后返回應用組件里需要的數據和方法即可。

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

OK,此時我們來觀察一下組件里的代碼。

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="請輸入您要搜索的內容"
          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>
  )
}

邏輯簡潔了許多。變成了簡單的同步代碼:通過一個方法獲取數據,并將數據渲染到 UI 組件。

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

一個組件變成了數據與UI的結合。我們分別將復雜的數據處理邏輯封裝在 hook 里,將復雜的UI交互邏輯封裝在基礎 UI 組件里,在使用時,利用他們的封裝結果進行組合,能夠簡單,高效的組合出復雜的頁面,這也是我們在實踐中最大的追求

這里有些人可能會有一些疑問,我只是把一些邏輯放在了另外的地方,代碼量最終不僅沒有減少,反而還變多了,這樣做的好處真的有那么大嗎?當然,因為我們封裝的 useFetch 和 List 組件,他們承載了大多數的復雜邏輯,并且只會在最開始的時候編寫一次,在以后的使用中,就直接引入使用就行了,這極大的簡化了后續的開發工作量,對工作效率的提高非常顯著

二、進一步思考

此時的封裝雖然足夠簡潔。但是沒有考慮復用。因此還需要進一步思考改進。

我們來分析一下場景:每一個需要信息展示的頁面,基本邏輯都是在初始化時,請求接口,獲得數據,然后展示信息。我們可以把不同情況的接口請求抽象成為一個接口,然后基于這個場景來思考不同頁面的請求的共性與差異。

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

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

const info = useFetch(searchApi)

不過我們此時還需要考慮的是,為了確保自定義 hook 的返回類型具備完整準確的類型推導,我們還需要約定傳入 api 的參數類型與返回類型。

因此,在定義 useFetch 時,我們先用 ts 約定 api 的具體類型,因為參數類型和返回值類型在封裝時都不確定,只能在具體的實參傳入之后才能明確,因此使用兩個泛型來分別表示參數類型和返回值類型。

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

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

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

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 
  }
}

因為在使用時,傳入的 api 函數已經具備了完善的類型,因此我們這種寫法可以借助 ts 內部的自動推導而簡化使用時在 ts 上的繁瑣。

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

雖然在使用層面沒有任何 ts 的痕跡,但是返回值的類型已經非常明確。

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

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

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

在實踐過程中,由于不同的團隊有不同的需求,你還需要根據自己的需求和項目實際情況做相應的細節調整,切記不要完整套用。

三、取舍

由于面試的影響,讓不少前端同行錯誤的把性能當成了實踐中最重要的標準。但其實工作中性能并不是最高的優先級。我們往往會在可接受的范圍之內,犧牲性能換取其他的便利。

例如,多一層函數封裝,其實也就意味著執行壓力多那么一點點。但是他可能換來的是開發效率的極大提高。

因此,在我們的課程案例決策當中,提供的方案并不會把性能當做第一準則,代碼的可讀性、可維護性、開發效率的優先級都會比性能更高。只要我們在寫代碼的過程中,非常明確的知道這種方式我們舍棄了什么,得到了什么,你權衡之后,愿意做出這樣的取舍,那么這樣的方式就是可以使用的。

當然,性能依然非常重要,如果你的頁面出現了卡頓,我們就應該思考一下,是不是對性能的犧牲有點過了頭。

責任編輯:姜華 來源: 這波能反殺
相關推薦

2023-11-30 07:45:11

useEffectReact

2017-05-19 10:03:31

AndroidBaseAdapter實踐

2017-05-18 12:36:16

android萬能適配器列表視圖

2022-06-06 09:28:36

ReactHook

2025-01-22 11:10:34

2010-08-12 09:45:33

jQuery自定義事件

2023-09-27 22:10:47

Vue.jsJavaScript

2021-02-23 08:01:01

HooksReact架構

2025-05-15 07:11:51

2023-06-28 08:05:46

場景vue3自定義

2015-02-12 15:33:43

微信SDK

2023-06-27 15:02:47

2015-02-12 15:38:26

微信SDK

2016-12-26 15:25:59

Android自定義View

2024-06-13 09:50:45

2016-02-26 14:57:50

飛象網

2016-11-16 21:55:55

源碼分析自定義view androi

2011-06-23 10:49:13

Qt 自定義信號

2022-04-24 15:17:56

鴻蒙操作系統

2013-04-01 14:35:10

Android開發Android自定義x
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 在线观看中文字幕 | 日韩精品一区二区三区中文字幕 | 国产精品色婷婷久久58 | 91视频91| 99在线免费视频 | 国产高清视频在线观看 | 亚洲电影一区二区三区 | 国产精品免费小视频 | av中文字幕在线 | 日韩毛片| 欧美日韩国产一区二区三区 | 国产精品一区一区三区 | 91色网站 | 一区二区中文字幕 | 免费在线一区二区 | 在线观看欧美日韩视频 | 久久婷婷香蕉热狠狠综合 | 亚洲精品中文字幕 | 亚洲高清av在线 | 亚洲视频免费 | 亚洲国产精品视频一区 | 久久婷婷麻豆国产91天堂 | 99精品热视频 | 日韩中文字幕区 | 亚洲成人毛片 | 天堂男人av| 亚洲精品乱码久久久久久9色 | 99九色| 成人深夜福利在线观看 | 亚洲欧美在线观看 | 国产精品一二三区 | 久久久久久99| 伊人免费视频二 | 99婷婷| 欧美午夜精品久久久久久浪潮 | 亚洲一区 中文字幕 | 香蕉国产在线视频 | 99国产精品久久久久老师 | 日韩中文字幕在线视频 | 欧美一区二区三区在线播放 | 亚洲天堂中文字幕 |