React Hook让无狀态组件拥有了许多只有有狀态组件的能力,如自更新能力(setState,使用useState),访问ref(使用useRef或useImperativeMethods),访问context(使用useContext),使用更高级的setState设置(useReducer),及进行相似生命周期的阶段性方法(useEffect或useLayoutEffect)。javascript
固然还有一些Hook,带来了一些新功能,如useCallback,这是对事件句柄进行缓存,useState的第二个返回值是dispatch,可是每次都是返回新的,使用useCallback,可让它使用上次的函数。在虚拟DOM更新过程当中,若是事件句柄相同,那么就不用每次都进行removeEventListner与addEventListner。最后就是useMemo,取得上次缓存的数据,它能够说是useCallback的另外一种形式。html
useState: setState
useReducer: setState
useRef: ref
useImperativeMethods: ref
useContext: context
useCallback:能够对setState的优化
useMemo: useCallback的变形
useLayoutEffect:相似componentDidMount/Update, componentWillUnmount
useEffect: 相似于setState(state, cb)中的cb,老是在整个更新周期的最后才执行
从上面的描述来看useEffect的时期是很是晚,能够保证页面是稳定下来再作事情。可是useEffect与useLayoutEffect与有狀态组件的生命周期钩子又有一点不同。java
useEffect(function(){ //dosomething return function(){ //dosomething } }, inputs)
useEffect与useLayoutEffect的第一个参数是一个函数(初始函数),这函数还会返回另外一个清理用的函数(清理函数,在官方文档中没有明确的文字,都注释使用了clean up的字眼,就姑且这样叫)。当某个无狀态组件要在某个阶段执行这些钩子,它会优先执行清理函数再执行初始函数。react
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width"> <script src="./react.development.js"></script> <script src="./react-dom.development.js"></script> <script type='text/javascript' src="./lib/babel.js"></script> </head> <body> <div id='root' class="root"> </div> <script type='text/babel'> var container = document.getElementById('root'); var {useState, useEffect, useLayoutEffect} = React; function Example() { const [count, setCount] = useState(0); const [text, setText] = useState(''); var a = useRef("xxx") useEffect(() => {//初始函数 console.log(a, 'useEffect') document.title = `You clicked ${count} times`; return () =>{//清理函数 console.log(a, 'end useEffect') document.title = `remove`; } }); useLayoutEffect(() => {//初始函数 console.log(a, 'useLayoutEffect') document.title = `You clicked ${count} times`; return () =>{//清理函数 console.log(a, 'end useLayoutEffect') document.title += '!!!'; } }); console.log(count, '更新Example') return ( <quoteblock> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> <input ref={a} value={text} onChange={function(e){ setText(e.target.value) }} /> <span>共{text.length}个字符</span><Child /> </quoteblock> ); } class App extends React.Component{ state = { aaa: 1 } onClick(){ this.setState(function(s){ return { aaa: s.aaa +1 } }) } componentDidMount(){ console.log("app mount") } componentDidUpdate(){ console.log("app update") } render(){ return <div>{this.state.aaa < 10 ? <Example />: null} <h1 onClick={this.onClick.bind(this)}>{ this.state.aaa}</h1> </div> } } class Child extends React.Component { componentDidMount(){ console.log("Child mount") } componentDidUpdate(){ console.log("Child update") } render(){ return <span>Child</span> } } ReactDOM.render(<App />, container) </script> </html>
初次渲染的界面与日志缓存
若是咱们向input输入内容,就会发现它每次都会先进行 useEffect与useLayout的清理函数,再执行他们的初始函数。而且发现useEffect的函数会在最后才执行,它会晚于包含它的父函数。咱们能够点击页面上的h1标签,就能够证实这一点。babel
点击h1会不断递增数字,到10时会销供Example这个无狀态组件与它的子组件Child。下面是数字到10时的界面与日志。app
在个人迷你React框架中是这样实现这两个钩子框架
export function useEffect(create, inputs) { return dispatcher.useEffect(create, inputs, PASSIVE, 'passive', 'unpassive'); } export function useLayoutEffect(create, inputs) { return dispatcher.useEffect(create, inputs, HOOK, 'layout', 'unlayout'); } export var dispatcher = { //略... useEffect(create, inputs, EffectTag, createList, destoryList) { let fiber = getCurrentFiber(); let cb = dispatcher.useCallbackOrMemo(create, inputs); if (fiber.effectTag % EffectTag) { fiber.effectTag *= EffectTag; } let updateQueue = fiber.updateQueue; let list = updateQueue[createList] || (updateQueue[createList] = []); updateQueue[destoryList] || (updateQueue[destoryList] = []); list.push(cb); }, //略... };
它们就是执行时机不同。dom
当目前React Hook仍是实验性质,不排除会改变。目前有9种钩子,其实以前有十种,useMutationEffect前不久已经完蛋了。useMemo与useCallback很相近,但以为useMemo的使用场合不多,不知它会不会废掉。useEffect很差用,不像useLayoutEffect那么明显能够与有狀态组件的生命周期钩子相对应。useImperativeMethods这个名字起得很差,可能之后也会调整。固然这只是个人见解。函数
React Hook是一个很棒的设计,它实际上是将有狀态组件的更新机制(setState/forceUpdate)的内部实现进行了更普遍的应用。当它的API稳定下来我会与你们分享它们更深层次的实现。