React hook是 React
团队发明的,用于在 Function
组件中引入状态管理和副作用。React Hooks使我们采用函数组件的写法编写 React
应用。因此,不再需要 Class
组件。
一、Hooks产生的动机: 1、方便重构函数组件 Hooks出现前,只有 Class
组件才能使用 state
状态和生命周期钩子函数,而生命周期函数是引入副作用(事件监听、接口请求)必不可少的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 import React , { Component } from 'react' ; class Counter extends Component { constructor (props ) { super (props); this .state = { count : 0 , }; } render ( ) { return ( <div > <p > You clicked {this.state.count} times</p > <button onClick ={() => this.setState({ count: this.state.count + 1 }) } > Click me </button > </div > ); } } export default Counter ;
如果一个组件即无状态又无副作用,就应该使用无状态组件 (stateless component
)。
无状态组件写法简洁,逻辑清晰的优点,在项目中大量使用。这也带来一个缺点: ** 每次需要状态或生命周期时,都需要将函数组件重构为类组件 **
有了 Hooks
之后,就不需要这种重构了。函数式组件支持通过 Hooks
读取状态和副作用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import React from 'react' ; function Counter ( ) { const [count, setCount] = React .useState (0 ); return ( <div > <p > You clicked {count} times</p > <button onClick ={() => setCount(count + 1)}> Click me </button > </div > ); } export default Counter ;
2、代码组织更好 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 import React from 'react' ; class MyComponent extends React.Component { componentDidMount ( ) { } componentWillUnmount ( ) { } ... } function MyComponent ( ) { React .useEffect (() => { }); React .useEffect (() => { }); ... }
在 Class
写法中:所有的 side-effects
方法都是按生命周期方法分组,代码比较散。
第二种写法明显更好,每个 side-effects
统一在一个钩子函数里面,同时每个钩子函数还提供了清理副作用的方法。
3、抽象地狱 Hooks出现之前:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import React from 'react' ;import { compose, withReducer } from 'recompose' ;import { withRouter } from 'react-router-dom' ; function App ({ history, state, dispatch } ) { return ( <ThemeContext.Consumer > {theme => <Content theme ={theme} > ... </Content > } </ThemeContext.Consumer > ); } export default compose ( withRouter, withReducer (reducer, initialState) )(App );
嵌套地狱:为完成指定的功能,多个组件嵌套到一起。导致代码可读性比较差,因为不知道究竟调用的是哪个父组件的功能。
Hooks出现后:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import React from 'react' ;import { useTheme } from 'styled-components' ;import { useRouter } from 'react-router-dom' ; function App ( ) { const theme = useTheme (); const history = useRouter (); const [state, dispatch] = React .useReducer (reducer, initialState); return ( <Content theme ={theme} > ... </Content > ); } export default App ;
4、Class 混乱? js提供了两种编程理念:面向对象编程(OOP)、函数式编程(FP)。React 同时支持两大编程方式。
一方面,React大量引入了函数式编程(如高阶函数、JS内置方法map/filter)以及不可变性,副作用等。这些编程本质上与React无关,属于编程范式,在React中被大量的开发者使用。
另一方面,React
使用 Class
创建组件,同时提供了一系列生命周期函数:
componentWillMount
componentDidMount
componentDidUpdate
componentWillUnmount
还支持自定义事件,需要通过 this.bind
来改变 this
指向。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 class Counter extends Component { state = { value : 0 }; onIncrement = () => { this .setState (state => ({ value : state.value + 1 })); }; onDecrement = () => { this .setState (state => ({ value : state.value - 1 })); }; render ( ) { return ( <div > {this.state.value} {/* WHY IS EVERYTHING AVAILABLE ON "THIS"??? */} <button onClick ={this.onIncrement} > +</button > <button onClick ={this.onDecrement} > -</button > </div > ) } }
二、一些常用Hooks用法 1、useState 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 function App ( ) { const [list, setList] = React .useState (INITIAL_LIST ); function onRemoveItem (id ) { const newList = list.filter (item => item.id !== id); setList (newList); } return ( <ul > {list.map(item => ( <li key ={item.id} > <a href ={item.url} > {item.title}</a > <button type ="button" onClick ={() => onRemoveItem(item.id)}> Remove </button > </li > ))} </ul > ); }
2、useEffect 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 import React from 'react' ; function App ( ) { const [isOn, setIsOn] = React .useState (false ); const [timer, setTimer] = React .useState (0 ); React .useEffect (() => { let interval; if (isOn) { interval = setInterval ( () => setTimer (timer => timer + 1 ), 1000 , ); } return () => clearInterval (interval); }, [isOn]); const onReset = ( ) => { setIsOn (false ); setTimer (0 ); }; return ( <div > {timer} {!isOn && ( <button type ="button" onClick ={() => setIsOn(true)}> Start </button > )} {isOn && ( <button type ="button" onClick ={() => setIsOn(false)}> Stop </button > )} <button type ="button" disabled ={timer === 0} onClick ={onReset} > Reset </button > </div > ); } export default App ;
三、自定义Hooks 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 import React from 'react' ; function useOffline ( ) { const [isOffline, setIsOffline] = React .useState (false ); function onOffline ( ) { setIsOffline (true ); } function onOnline ( ) { setIsOffline (false ); } React .useEffect (() => { window .addEventListener ('offline' , onOffline); window .addEventListener ('online' , onOnline); return () => { window .removeEventListener ('offline' , onOffline); window .removeEventListener ('online' , onOnline); }; }, []); return isOffline; } function App ( ) { const isOffline = useOffline (); if (isOffline) { return <div > Sorry, you are offline ...</div > ; } return <div > You are online!</div > ; } export default App ;
React hook的最大优点是提供代码复用性,这极有可能形成一个自定义 hook 的生态,可以从npm上下载所需要的 hook 来提升开发效率。
四、深入学习Hooks系列
useState
useReducer
useEffect
useCallback
useMemo
useRef
useContext
全文完。