const
App = () => {
const showCounter = useBoolean(true);
const counter = useNumber(0);
return (
<button onClick={counter.increase}> increase button>
{showCounter.value && {counter.value} span>}
<button onClick={counter.decrease}> decrease button>
div>
);
};
初步印象:大致与原始的basic hooks有点不同的是,useState返回一个数组,分别是 值
与 操作
,而react-hanger提供的API貌似是将 值
和 一些操作
封装到一个对象中,比如 counter
就是一个 {value:count,increase:setCount(count+1),decrease:setCount(count-1)}
的对象。
还有更多的操作方法可以看react-hanger的sandbox:https://codesandbox.io/s/44m70xm70
react-hanger源码浅析
其实翻看了react-hanger的源码之后会发现,react-hanger一共引用了四个React内置的Hook,
import { useCallback, useEffect, useRef, useState } from "react";
然后返回一些“轮子”hooks,包括 useNumber
、 useArray
、 useBoolean
等等。
这些轮子可以大致分为两类:封装Hook和拆分Hook。
封装Hook
比如 useStateful
、 useNumber
、 useArray
、 useBoolean
都是对内置Hook useState
的封装。
useStateful
export const useStateful = initial => {
const [value, setValue] = useState(initial);
return {
value,
setValue
};
};
利用ES6的解构赋值,将 useState
返回的数组封装成一个对象重新返回,方便调用。
useNumber
export const useNumber = (
initial,
{ upperLimit, lowerLimit, loop, step = 1 } = {}
) => {
const [value, setValue] = useState(initial);
return {
value,
setValue,
increase: useCallback(i => {
setValue
(...);
}, []),
decrease: useCallback(d => {
setValue(...);
}, [])
};
};
useNumber
接收一个initial number和一个配置项对象,在内部是通过对initial number进行useState Hook,返回一个对象,除了基本的 value
和 setValue
,还有两个方法 increase
和 decrease
。这两个方法都是用 useCallback
对 setValue
进行的进一步封装。
而 useCallback
是一个比较重要的内置Hook, useCallback
的可以于缓存了每次渲染时 inline callback 的实例,在第二个参数数组内的值发生更改时才会更改。
这样可以配合上子组件的 shouldComponentUpdate
或者 useMemo
起到减少不必要的渲染的作用。
而第二个参数为空数组的意思就是告诉React不管参数如何都要记忆。
useArray & useBoolean & useInput
至于 useArray
、 useBoolean
、 useInput
这三个hook可以说和 useNumber
大同小异,都是需要一个传入的initial值,在hook内部通过 useState
初始化,再返回一些常用的操作方法。
这里的 useInput
是针对于受控组件,所以不需要 useRef
。
useSetState
export const useSetState = initialValue => {
const { value, setValue } = useStateful(initialValue);
return {
setState: useCallback(v => {
return setValue(oldValue => ({
...oldValue,
...(typeof v === "function" ? v(oldValue) : v)
}));
}, []),
state: value
};
};
Unlike the setState
method found in class components, useState
does not automatically merge update objects.
与类组件中的setState方法不同,useState不会自动合并更新对象。
熟悉React Hook的同学看了代码就知道这个hook是封装了什么了,因为useState返回的类似于 setCount
的方法不会自动合并更新对象。这个hook帮助大家可以获得一个可以merge之前value的Hook型 setState
。
拆分Hook
上述几个算是封装hook,那么下面的几个就可以算是拆分hook,对 useEffect
更精细化的处理。
useOnMount & useOnUnmount
众所周知, useEffect
是被用来处理一些原先放在class组件中生命周期函数的副作用,比如 componentDidMount
、 componentDidUpdate
、 componentWillUnmount
,集合而成的一个Hook。
理论上,在每次渲染后都会触发 useEffect
的效果,但是如果我只想在didmount里或者只想在willunmount里做一下事情,该怎么办?
这时就用到了 useEffect
的一个特点:第二个参数为效果依赖的值数组,也就是说只有当数组内的值变化才会触发 useEffect
,
useEffect(
() => {
const subscription = props.source.subscribe();
return () => {
subscription.unsubscribe();
};
},
[props.source],
);
而如果第二个参数为一个空数组的时候,则相当于告诉React你的效果不依赖于组件中的任何值,因此该效果只能在mount上运行并在unmount上清理,它不会在更新时运行。
export const useOnUnmount = onUnmount =>
useEffect(() => {
return () => onUnmount && onUnmount();
}, []);
export const useOnMount = onMount =>
useEffect(() => {
onMount