react-hooks HOOKS
- hooks概念在React Conf 2018被提出來,并將在未來的版本中被引入,hooks遵循函數(shù)式編程的理念,主旨是在函數(shù)組件中引入類組件中的狀態(tài)和生命周期,并且這些狀態(tài)和生命周期函數(shù)也可以被抽離,實(shí)現(xiàn)復(fù)用的同時(shí),減少函數(shù)組件的復(fù)雜性和易用性。
- 函數(shù)組件 (functional component) 內(nèi)部能夠”鉤住“ React 內(nèi)部的 state 和 life-cycles。
- 真正功能強(qiáng)大的地方是使我們能夠更輕松地復(fù)用組件邏輯(custom hooks)
- 讓FunctionalComponent具有ClassComponent的功能
設(shè)計(jì)Hooks主要是解決ClassComponent的幾個(gè)問題:
- 很難復(fù)用邏輯(只能用HOC,或者render props),會(huì)導(dǎo)致組件樹層級(jí)很深
- 會(huì)產(chǎn)生巨大的組件(指很多代碼必須寫在類里面)
- 類組件很難理解,比如方法需要bind,this指向不明確
npm install react@16.7.0-alpha.2
npm install react-dom@16.7.0-alpha.2
npm install eslint-plugin-react-hooks@next
// Your ESLint configuration
{
"plugins": [
// ...
"react-hooks"
],
"rules": {
// ...
"react-hooks/rules-of-hooks": "error"
}
}
Hooks API Reference
** Basic Hooks **
useState
useEffect
useContext
** Additional Hooks **
useReducer
useCallback
useMemo
useRef
useImperativeMethods
useMutationEffect
useLayoutEffect
Rules of Hooks
- 只能在頂層調(diào)用Hooks 。不要在循環(huán),條件或嵌套函數(shù)中調(diào)用Hook
- 只能在functional component中使用
State Hook
定義組件狀態(tài)
- 避免組件的 state 結(jié)構(gòu)過于臃腫,能夠獨(dú)立處理每個(gè) state
- 寫法非常直觀,一眼就可以看出和這個(gè) state 相關(guān)的兩個(gè)變量
const useStateExample = () => {
const [count, setCount] = useState(0);
const [list, setList] = useState([]);
const [object, setObject] = useState({ name: 'lishishi', age: 28 });
}
State Hooks的定義必須在函數(shù)組件的最高一級(jí),不能在嵌套,循環(huán)等語句中使用
一個(gè)函數(shù)組件可以存在多個(gè)State Hooks,并且useState返回的是一個(gè)數(shù)組,數(shù)組的每一個(gè)元素是沒有標(biāo)識(shí)信息的,完全依靠調(diào)用useState的順序來確定哪個(gè)狀態(tài)對(duì)應(yīng)于哪個(gè)變量,所以必須保證使用useState在函數(shù)組件的最外層
最主要的原因就是你不能確保這些條件語句每次執(zhí)行的次數(shù)是一樣的,也就是說如果第一次我們創(chuàng)建了state1 => hook1, state2 => hook2, state3 => hook3這樣的對(duì)應(yīng)關(guān)系之后,下一次執(zhí)行因?yàn)閟omething條件沒達(dá)成,導(dǎo)致useState(1)沒有執(zhí)行,那么運(yùn)行useState(2)的時(shí)候,拿到的hook對(duì)象是state1的,那么整個(gè)邏輯就亂套了,所以這個(gè)條件是必須要遵守的!
const useStateExample = () => {
if(Math.random() > 1) {
const [count, setCount] = useState(0);
const [list, setList] = useState([]);
}else {
const [object, setObject] = useState({ name: 'lishishi', age: 28 });
}
}
Effect Hook
實(shí)現(xiàn)生命周期 (life-cycles)
- 類似 redux 中的 subscribe,每當(dāng) React 因?yàn)?state 或是 props 而重新 render 的之后,就會(huì)觸發(fā) useEffect 里的這個(gè) callback listener(在第一次 render 和每次 update 后觸發(fā))
- 在生命周期內(nèi)做的操作很多都會(huì)產(chǎn)生一些 side-effect(副作用)的操作,比如更新 DOM,fetch 數(shù)據(jù),等等。
useEffect(() => {
//componentDidMount和componentDidUpdate周期的函數(shù)體
return ()=>{
//componentWillUnmount周期的函數(shù)體
}
})
// ---------------------
useEffect(() => {
//僅在componentDidMount的時(shí)候執(zhí)行
},[]);
// -----------------------
useEffect(() => {
//僅在componentDidMount的時(shí)候執(zhí)行
//只有stateName\props.id的值發(fā)生改變
},[stateName,props.id]);
componentDidUpdate(prevProps, prevState) {
if (prevState.count !== this.state.count) {
}
if (prevProps.id !== this.props.id) {
}
}
- useEffect函數(shù)必須位于函數(shù)組件的最高一級(jí)
useState('Mary')
useEffect(persistForm)
useState('Poppins')
useEffect(updateTitle)
Context Hook
替代了 <Context.Consumer> 使用 render props 的寫法,使組件樹更加簡潔。
Reducer Hook
相當(dāng)于組件自帶的 redux reducer,負(fù)責(zé)接收 dispatch 分發(fā)的 action 并更新 state
配合hooks重新實(shí)現(xiàn)react-redux
useReducer + useContext 鉤子上進(jìn)行一層很簡單的封裝以達(dá)到和以往 react-redux \ redux-thunk \ redux-logger 類似的功能
npm install react-hooks-redux
React.memo() React 16.6.0
- PureComponent 要依靠 class 才能使用。而 React.memo() 可以和 functional component 一起使用
const MySnowyComponent = React.memo(function MyComponent(props) {
// only renders if props have changed!
});
// can also be an es6 arrow function
const OtherSnowy = React.memo(props => {
return <div>my memoized component</div>;
});
// and even shorter with implicit return
const ImplicitSnowy = React.memo(props => (
<div>implicit memoized component</div>
));
useRef
useCallBack
- e.g. shouldComponentUpdate ; This is useful when passing callbacks to optimized child components that rely on reference equality to prevent unnecessary renders
const memoizedCallback = useCallback(
() => {
doSomething(a, b);
},
[a, b],
);
useMemo
- useMemo will only recompute the memoized value when one of the inputs has changed
- useCallback(fn, inputs) is equivalent to useMemo(() => fn, inputs)
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
useImperativeMethods
- useImperativeMethods customizes the instance value that is exposed to parent components when using ref
- useImperativeMethods(ref, createInstance, [inputs])
useMutationEffect useLayoutEffect useEffect 差別
useMutationEffect
It fires synchronously before Layout phase i.e. during the same phase that React performs its DOM mutations. Use it to perform blocking custom DOM mutations without taking any DOM measurement/reading layout.useLayoutEffect
It fires synchronously after all DOM mutations but before Paint phase. Use this to read layout(styles or layout information) from the DOM and then perform blocking custom DOM mutations based on layout.useEffect
It runs after the render is committed to the screen i.e. after Layout and Paint phase. Use this whenever possible to avoid blocking visual updates
注意
- 目前react-hot-loader不能和hooks一起使用