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有一些了解
什么时候会触发组件渲染
组件中的状态改变的时候,
- useState改变状态
- 子组件调用父组件的方法使父组件的状态改变
- props改变
- 父组件重新渲染
- 生成该组件的工厂函数重新执行(如改工厂函数放在useCallback中,依赖值发生变化)
useEffect的两个参数
effect
该参数接收一个函数,该函数返回一个销毁函数(指return返回的cleanup函数),如果 useEffect 第一个参数传入 async,返回值则变成了 Promise,会导致 react 在调用销毁函数的时候报错 :function.apply is undefined
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>
);
};
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>
);
};
同样的,这里也会算上初始渲染,如果想不考虑首次渲染,可以调整第二个参数的范围
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>
)
}
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>
)
}
可以看到后面点击reRender时useFirstMountState()
都是返回false
useUpdateEffect
非首次渲染时执行
type {
effect:EffectCallback,
deps:DependencyList,
}
对这个项目的介绍就到这里啦,如果后面有更新会继续的,此项目的github地址https://github.com/plutoLam/h...,使用bruce-app搭建,rollup进行打包
转载自:https://segmentfault.com/a/1190000041784003