博客 / 詳情

返回

react hook的最強拓展——useEffect篇

前言

隨着react16.8的發佈,hook新特性隨之而來,hook的到來讓function組件煥發出強大的能力,足矣取代之前的class組件。函數式組件依靠useState、useEffect等hook實現變量狀態維持、抽離副作用等功能。雖然原生的useEffect具有強大的功能,但是那些常用的寫法每次都要手動復現一次,不但影響開發效率,而且容易出錯。

下面我會給大家推薦一個npm第三方庫,它將與有關useEffect常用的寫法都封裝起來,絕對可以助力你的開發,

讓你的開發如魚得水。

這個庫的名稱是pluto-hooks,在npm官網上也可以搜索到其相關信息,下面是npm地址

https://www.npmjs.com/package...

此項目大部分hook都是和useEffect有關,當然也有其他,開發者們可以自行選擇使用,下面介紹一下此庫的使用方法

安裝

首先先要在react項目中安裝,推薦使用yarn安裝

yarn

yarn add pluto-hooks

npm

npm i pluto-hooks

使用

在react項目中直接引用即可

import { useEffectOnece } from "pluto-hooks";

useEffectOnece(() => {
  console.log("觸發");
});

前置知識

要了解下面的用法,你得對react組件(這裏只用函數式組件)及hook有一些瞭解

什麼時候會觸發組件渲染
  1. 組件中的狀態改變的時候,

    • useState改變狀態
    • 子組件調用父組件的方法使父組件的狀態改變
    • props改變
  2. 父組件重新渲染
  3. 生成該組件的工廠函數重新執行(如改工廠函數放在useCallback中,依賴值發生變化)
useEffect的兩個參數
  1. effect

    該參數接收一個函數,該函數返回一個銷燬函數(指return返回的cleanup函數),如果 useEffect 第一個參數傳入 async,返回值則變成了 Promise,會導致 react 在調用銷燬函數的時候報錯 :function.apply is undefined

  2. deps

    改參數接收一個數組,數組中存放useEffect的依賴值,當頁面重新渲染時react會對比前一個deps和新deps,這裏是用的淺比較對deps中的每個元素進行比較,所以當deps=[{}]時effect每次都會執行,因為{} !=== {}

hooks介紹

useCountUpdateEffect

第n次渲染並且deps變化才執行effect

有時候我們在項目開發中需要在組件渲染到固定次數時觸發某個方法,如使用input輸入文本時,每次敲擊鍵盤都會觸發一次render,我們可以使用useCountUpdateEffect控制effect的觸發時機

注:第一次渲染也算一次

示例
import React, { useState } from "react";
import useCountUpdateEffect from "../index";

export default () => {
    const [text, setText] = useState('');

  function inputChange(e){
    setText(e.target.value)
  }

    useCountUpdateEffect(() => {
        console.log('useCountUpdateEffect執行');
    },10);

    return (
        <div>
            <p>{text}</p>
            <p>
                <input type="text" onChange={inputChange} />
            </p>
        </div>
    );
};

countUpdate.gif

useRangeUpdateEffect

在組件渲染次數為[range[0],range[1]]之間執行

此hook為useCountUpdateEffect的升級版,有時候我們不一定要在某個渲染次數時觸發回調函數,而是要在某個範圍內連續觸發

其中第二個參數傳入一個數組range,range[0]為左邊界,range[1]為右邊界,若傳入[,right],則默認為[0,right],如果傳入[left,],則大於left(包括left)的渲染次數都會觸發effect,若傳入[],則與useEffect沒有區別

第三個參數為依賴deps

示例
import React, { useState } from "react";
import useRangeUpdateEffect from "../index";

export default () => {
    const [text, setText] = useState('');

  function inputChange(e){
    setText(e.target.value)
  }

    useRangeUpdateEffect(() => {
        console.log('useRangeUpdateEffect');
    },[5,10]);

    return (
        <div>
            <p>{text}</p>
            <p>
                <input type="text" onChange={inputChange} />
            </p>
        </div>
    );
};

range.gif

同樣的,這裏也會算上初始渲染,如果想不考慮首次渲染,可以調整第二個參數的範圍

useCompareEffect

type {
    effect:EffectCallback,
    deps:DependencyList,
    depsEqual:(oldDeps:DependencyList,nextDeps:DependencyList) => boolean
}

正如我們前置知識講到的,useEffect默認只會對deps進行淺比較,但是使用useCompareEffect我們就可以自定義deps的比較

第三個參數為比較函數,depsEqual的第一個參數是舊的deps數組,第二個參數為新的deps數組,該函數返回一個boolean值,為true時不執行effect,反之執行

示例
import React, { useState } from "react";
import useCompareEffect from "../index"

export default function demo() {
  const [count1, setCount1] = useState(0)
  const [count2, setCount2] = useState(0)
  const [count, setCount] = useState(0)

  const compareFn = (old,next) => {
    console.log('old,next: ', old,next);
    return old[1] === next[1]
  }

  useCompareEffect(() => {
    // 只有count2改變時會觸發
    setCount(state => state + 1)
  }, [count1, count2],compareFn)

  return (
    <div>
      <h2>{count}</h2>
      <h2>{count1}</h2>
      <h2>{count2}</h2>
      <button onClick={() => {setCount1(state => state + 1)}}>count1</button>
      <button onClick={() => {setCount2(state => state + 1)}}>count2</button>
    </div>
  )
}

compare.gif

useDeepCompareEffect

此hook基於上面的useCompareEffect,當組件重新渲染時執行useDeepCompareEffect,該hook將對deps進行深比較,若相等則執行effect

type {
    effect:EffectCallback,
    deps:DependencyList,
}

useAsyncEffect

看過前置知識的小夥伴可能還記得,useEffect的第一個參數是不能傳入async函數的,如果真的要使用async函數,可以調用useAsyncEffect

示例
import React, { useState } from "react";
import useAsyncEffect from "../index"

export default () => {

  const fn = () => {
    return new Promise(resolve => {
      setTimeout(() => {
        resolve()
      }, 3000)
    })
  }

  useAsyncEffect(async () => {
    console.log('等待...');
    await fn()
    console.log('完成');
  }, []);
};

以上代碼將會在組件初始化時顯示”等待...“,3秒後顯示”完成“

useDebounceFn

處理防抖函數

const {
  run,
  cancel,
  flush
} = useDebounceFn(
  fn: (...args: any[]) => any,
  options?: Options
);

run, cancel, flush為可執行函數

run:開始防抖地執行fn,傳入run的參數會傳到fn

cancel:取消執行fn

flush:立即執行fn,不用等到wait時間結束

useDebounceEffect

type {
    effect:EffectCallback,
    deps?:DependencyList,
    options:DebounceOptions
}

將傳入的Effect帶上防抖,options可以配置防抖參數

options參數(默認值):

[options.wait=1000] (number): 等待時間,單位為毫秒。
[options.leading=false] (boolean): 指定在延遲開始前調用。
[options.maxWait] (number): 設置 func 允許被延遲的最大值。
[options.trailing=true] (boolean): 指定在延遲結束後調用。

useThrottleFn

處理節流函數的hook

const {
  run,
  cancel,
  flush
} = useThrottleFn(
  fn: (...args: any[]) => any,
  options?: Options
);

options參數:

[options.wait=1000] (number): 等待時間,單位為毫秒。
[options.leading=true] (boolean): 指定調用在節流開始前。
[options.trailing=true] (boolean): 指定調用在節流結束後。

run, cancel, flush為可執行函數

run:開始防抖地執行fn,傳入run的參數會傳到fn

cancel:取消執行fn

flush:立即執行fn,不用等到wait時間結束

useThrottleEffect

將傳入的Effect帶上節流功能,options可以配置節流參數

type {
    effect:EffectCallback,
    deps:DependencyList,
    options:ThrottleOptions
}

options參數:

[options.wait=1000] (number): 等待時間,單位為毫秒。
[options.leading=true] (boolean): 指定調用在節流開始前。
[options.trailing=true] (boolean): 指定調用在節流結束後。

useEffectOnce

只在第一次渲染時執行

type {
    effect:EffectCallback,
}

useFirstMountState

判斷改組件是否是第一次渲染,返回Boolean

示例
import React, { useEffect, useState } from "react";
import useFirstMountState from "../index"

export default () => {
  const [count, setCount] = useState(0);
    const isfirst = useFirstMountState()
    console.log('isfirst: ', isfirst);

  return (
    <div>
      <div>first:{isfirst}</div>
      <p>
        <button type="button" onClick={() => setCount((c) => c + 1)}>
          reRender
        </button>
      </p>
    </div>
  )
}

first.gif

可以看到後面點擊reRender時useFirstMountState()都是返回false

useUpdateEffect

非首次渲染時執行

type {
    effect:EffectCallback,
    deps:DependencyList,
}

對這個項目的介紹就到這裏啦,如果後面有更新會繼續的,此項目的github地址https://github.com/plutoLam/h...,使用bruce-app搭建,rollup進行打包

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.