千呼万唤始出来,React Hooks终于在React 16.8版本中发布稳定版了。最近逛github发现了一个颇有意思的库:react-hanger。html
若是对Hooks还不怎么了解的同窗,建议去看一下官方文档:Introducing Hooks.react
咱们都知道,在Hooks以前,开发react组件主要是class组件和function组件。function组件没有state,因此也叫SFC(stateless functional component),简单的将props映射成view;class组件有state,可以处理更加复杂的逻辑。可是基于class的组建并非完美的,总结起来就像Dan说的那样,有三个主要的问题:git
为了解决上面的这三个问题,react hooks提案登场了,它有如下几个特色:github
Hooks 容许你在不编写 class 的状况下使用状态(state)和其余 React 特性。 你还能够构建本身的 Hooks, 跨组件共享可重用的有状态逻辑。数组
如今React中内置的Hooks有:缓存
固然了,授之以鱼不如授之以渔,React官方也提供了教你如何封装本身Hook的文档Building Your Own Hooks,有兴趣的小伙伴能够去阅读一下。bash
大体的看了下react-hanger的源码以后发现,这个库实际上是对React Hooks API的适用性封装。暴露一些更经常使用的Hooks节省你们造轮子的工做量。babel
React的核心开发者Dan看到这个库也作了评价:less
一个对Hooks的隐喻。你能够将你的state“挂起”在你的function component上,等你回来的时候,它就挂在那。函数
本文写做时,react-hanger的Usage里提供了6个API,从名字里就能够看出这些Hook都是作什么的(Hooks都以"use"开头,这是一种约定),
import {
useInput,
useBoolean,
useNumber,
useArray,
useOnMount,
useOnUnmount
} from "react-hanger";
复制代码
使用起来也很简单,好比useNumber
const App = () => {
const showCounter = useBoolean(true);
const counter = useNumber(0);
return (
<div>
<button onClick={counter.increase}> increase </button>
{showCounter.value && <span> {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内置的Hook,
import { useCallback, useEffect, useRef, useState } from "react";
复制代码
而后返回一些“轮子”hooks,包括useNumber
、useArray
、useBoolean
等等。
这些轮子能够大体分为两类:封装Hook和拆分Hook。
好比useStateful
、useNumber
、useArray
、useBoolean
都是对内置HookuseState
的封装。
export const useStateful = initial => {
const [value, setValue] = useState(initial);
return {
value,
setValue
};
};
复制代码
利用ES6的解构赋值,将useState
返回的数组封装成一个对象从新返回,方便调用。
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
这三个hook能够说和useNumber
大同小异,都是须要一个传入的initial值,在hook内部经过useState
初始化,再返回一些经常使用的操做方法。
这里的useInput
是针对于受控组件,因此不须要useRef
。
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,对useEffect
更精细化的处理。
众所周知,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 && onMount();
}, []);
复制代码
因此useOnMount
的实现就很是简单,在useEffect
内执行onMount函数且第二个参数是[]
,useOnUnmount
的实现则是返回onUnmount函数且第二个参数是[]
。
export const useLifecycleHooks = ({ onMount, onUnmount }) =>
useEffect(() => {
onMount && onMount();
return () => onUnmount && onUnmount();
}, []);
复制代码
useLifecycleHooks
则是对useOnUnmount
和useOnMount
的整合,在useEffect
的第二个参数为[]
的状况下,执行onMount和返回onUnmount。
export const useLogger = (name, props) => {
useLifecycleHooks({
onMount: () => console.log(`${name} has mounted`),
onUnmount: () => console.log(`${name} has unmounted`)
});
useEffect(() => {
console.log("Props updated", props);
});
};
复制代码
useLogger
算是一个为hook commponent封装的log插件,经过在useLifecycleHooks
内传入onMount和onUnmount打印日志的函数,以后再经过原生的默认useEffect
不传递第二个参数来实如今更新过程当中打印日志。
export const usePrevious = value => {
const ref = useRef();
useEffect(() => {
ref.current = value;
});
return ref.current;
};
复制代码
usePrevious则能够获取以前的props或者state,来自于React的官方文档。
其实react-hanger的源代码都很简洁,对源码感兴趣的同窗请看:https://github.com/kitze/react-hanger/blob/master/src/index.js,更多的是这个库的实现大部分都是用了React Hook的思想,但愿你们能够经过本文加深对React Hook的理解和认识。