React Hooks 是 React 在16.8版本中更新的新特性,在 React 中一直提倡使用函數組件,老版本中函數組件沒有組件實例,沒有 state,沒有生命週期函數,致使不少狀況不得不使用類組件,可是 Hooks 出來後咱們能夠在不使用類組件的狀況下使用state及其餘React特性!react
import React, { useState } from 'react';
function Example() {
// 聲明一個叫 "count" 的 state 變量
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
複製代碼
由於useState的更新函數會直接替換老的state,因此咱們在對對象或者數組的state作增刪的時候不能像之前直接對數組使用push,pop,splice等直接改變數組的方法ajax
錯誤示例:redux
import React, { useState } from "react";
function Comment() {
const [counts, setCounts] = useState([1, 2]);
const handleAdd = () => {
const randomCount = Math.round(Math.random()*100)
// 在此地方咱們使用push增長一個隨機數,程序報錯
setCounts(counts.push(randomCount))
}
return (
<div>
{counts.map((count) => (
<div key={count}>{count}</div>
))}
<button onClick={handleAdd}>增長</button>
</div>
);
}
export default Comment;
複製代碼
正確的方法應該是使用數組解構生成一個新數組,在數組後面加上咱們新增的隨機數達成數組新增項,使用filter數組過濾方法來實現咱們刪除其中項的操做。數組
數組新增項:瀏覽器
import React, { useState } from "react";
function Comment() {
const [counts, setCounts] = useState([1, 2]);
const handleAdd = () => {
const randomCount = Math.round(Math.random()*100)
// 在此咱們用數組結構生成新數組,並在後面加上咱們要新增的隨機數
setCounts([
...counts,
randomCount
])
}
return (
<div>
{counts.map((count) => (
<div key={count}>{count}</div>
))}
<button onClick={handleAdd}>增長</button>
</div>
);
}
export default Comment;
複製代碼
刪除其中項緩存
import React, { useState } from "react";
function Comment() {
const [counts, setCounts] = useState([1, 2, 3, 4]);
const handleDel = () => {
// 使用數組filter方法,過濾刪除其中不須要的項
setCounts(counts.filter((count, index) => index !== counts.length - 1))
}
return (
<div>
{counts.map((count) => (
<div key={count}>{count}</div>
))}
<button onClick={handleDel}>刪除</button>
</div>
);
}
export default Comment;
複製代碼
此外還有一個方法是相似之前使用redux的reducer中對老的數組對象作深拷貝,而後作增刪操做,最後返回性能優化
import React, { useState } from "react";
function Comment() {
const [counts, setCounts] = useState([1, 2]);
const handleAdd = () => {
setCounts(counts => {
const randomCount = Math.round(Math.random()*100)
// 簡單使用JSON.parse及JSON.stringify深拷貝一個新的數組和對象(實際項目中建議本身寫遞歸深拷貝函數),而後對其操做返回
let newCounts = JSON.parse(JSON.stringify(counts))
newCounts.push(randomCount)
return newCounts
})
}
return (
<div>
{counts.map((count) => (
<div key={count}>{count}</div>
))}
<button onClick={handleAdd}>增長</button>
</div>
);
}
export default Comment;
複製代碼
當咱們先執行異步增長函數(handleSyncAdd),再執行同步函數(handleAdd),同步執行完畢再執行異步時,異步函數裏面的count爲以前執行時閉包裏面的值(0),錯誤示例:bash
import React, { useState } from "react";
function Comment() {
const [count, setCount] = useState(0);
const handleAdd = () => setCount(count + 1);
const handleSyncAdd = () => {
setTimeout(() => {
// 獲取的是閉包中的state
setCount(count + 1);
}, 1000);
};
return (
<div>
<p>{count}</p>
<button onClick={handleAdd}>增長</button>
<button onClick={handleSyncAdd}>異步增長</button>
</div>
);
}
export default Comment;
複製代碼
這種狀況咱們要使用回調式函數更新閉包
正確示例:dom
import React, { useState } from "react";
function Comment() {
const [count, setCount] = useState(0);
const handleAdd = () => setCount(count + 1);
const handleSyncAdd = () => {
setTimeout(() => {
// 改爲回調函數更新,每次回調函數執行時會接收以前的state,而不是閉包中的state
setCount(count => count + 1);
}, 1000);
};
return (
<div>
<p>{count}</p>
<button onClick={handleAdd}>增長</button>
<button onClick={handleSyncAdd}>異步增長</button>
</div>
);
}
export default Comment;
複製代碼
componentDidMount
、ComponentDidUpdate
和componentWillUnmount
的功能呢,只不過被合併成爲一個APIcomponentDidMount
或 componentDidUpdate
不一樣的是,使用 useEffect
不會阻塞瀏覽器更新屏幕,這讓你的應用看起來響應更快。大多數狀況下,effect 不須要同步地執行。在個別狀況下(例如測量佈局),有單獨的 useLayoutEffect
供你使用,其 API 與 useEffect
相同。直接使用useEffect傳入一個回調函數,會在組件初次渲染及每次更新渲染時執行
import React, { useState, useEffect } from 'react'
function Parent() {
const [count, setCount] = useState(0)
const handleAdd = () => setCount(count + 1)
// 使用useEffect傳入一個回調函數使用類組件componentDidMount和componentDidUpdate功能
useEffect(() => {
console.log('parent effect');
})
return (
<div>
parent, {count}
<button onClick={handleAdd}>增長</button>
</div>
)
}
export default Parent
複製代碼
不少時候咱們只須要組件初次加載作一些事,如ajax獲取數據等,咱們只須要在useEfffect的第二個參數傳入一個空數組,這個數組的意思是數組裏面監聽的值發生更新update時執行effect
import React, { useState, useEffect } from 'react'
function Parent() {
const [count, setCount] = useState(0)
const handleAdd = () => setCount(count + 1)
// 第二個參數傳入空數組,不須要根據其餘值執行effect,只會在組件初次加載執行
useEffect(() => {
console.log('parent didMount');
}, [])
return (
<div>
parent, {count}
<button onClick={handleAdd}>增長</button>
</div>
)
}
export default Parent
複製代碼
也能夠在第二個數組中傳入值,代表根據這個值update時執行effect
import React, { useState, useEffect } from 'react'
function Parent() {
const [count, setCount] = useState(0)
const handleAdd = () => setCount(count + 1)
// 第二個參數傳入含有count的數組,count更新時執行effect
useEffect(() => {
console.log('count update');
}, [count])
return (
<div>
parent, {count}
<button onClick={handleAdd}>增長</button>
</div>
)
}
export default Parent
複製代碼
在項目中咱們須要在組件卸載時清除定時器、監聽等,使用useEffect返回一個函數,這個函數會在組件卸載時調用完成componentWillUnmout的功能
import React, { useState, useEffect } from 'react'
function Parent() {
const [count, setCount] = useState(0)
const handleAdd = () => setCount(count + 1)
// 在useEffect中返回一個函數完成componentWillUnmoun的功能
useEffect(() => {
console.log('component mount');
return () => {
console.log('component unmount');
}
})
return (
<div>
parent, {count}
<button onClick={handleAdd}>增長</button>
</div>
)
}
export default Parent
複製代碼
useMemo能夠初略理解爲Vue中的計算屬性,在依賴的某一屬性改變的時候自動執行裏面的計算並返回最終的值(並緩存,依賴性改變時才從新計算),對於性能消耗比較大的必定要使用useMemo否則每次更新都會從新計算。
示例:
import React, { useState, useMemo } from 'react'
function Parent() {
const [count, setCount] = useState(0)
const [price, setPrice] = useState(1)
const handleCountAdd = () => setCount(count + 1)
const handlePriceAdd = () => setPrice(price + 1)
// 使用useMemo在count和price改變時自動計算總價
const all = useMemo(() => count * price, [count, price])
return (
<div>
parent, {count}
<button onClick={handleCountAdd}>增長數量</button>
<button onClick={handlePriceAdd}>增長價格</button>
<p>count: {count}, price: {price} all: {all}</p>
</div>
)
}
export default Parent
複製代碼
useCallback不一樣於useMemo的是,useMemo是緩存的值,useCallback是緩存的函數,父組件給子組件傳遞參數爲普通函數時,父組件每次更新子組件都會更新,可是大部分狀況子組件更新是不必的,這時候咱們用useCallback來定義函數,並把這個函數傳遞給子組件,子組件就會根據依賴項再更新了
示例:
import React, { useState, useCallback, useEffect } from 'react';
function Parent() {
const [count, setCount] = useState(1);
const [val, setVal] = useState('');
const callback = useCallback(() => {
return count;
}, [count]);
return <div>
<h4>{count}</h4>
<Child callback={callback}/>
<div>
<button onClick={() => setCount(count + 1)}>+</button>
<input value={val} onChange={event => setVal(event.target.value)}/>
</div>
</div>;
}
function Child({ callback }) {
const [count, setCount] = useState(() => callback());
useEffect(() => {
console.log(123);
setCount(callback());
}, [callback]);
return <div>
{count}
</div>
}
export default Parent
複製代碼
useReducer和redux中reducer相似,useState 的替代方案。它接收一個形如 (state, action) => newState 的 reducer,並返回當前的 state 以及與其配套的 dispatch 方法。(若是你熟悉 Redux 的話,就已經知道它如何工做了。)
在某些場景下,useReducer 會比 useState 更適用,例如 state 邏輯較複雜且包含多個子值,或者下一個 state 依賴於以前的 state 等。而且,使用 useReducer 還能給那些會觸發深更新的組件作性能優化,由於你能夠向子組件傳遞 dispatch 而不是回調函數 。
import React, { useReducer } from 'react'
function Parent() {
const reducer = (state, action) => {
switch (action.type) {
case 'add':
return {count: state.count + 1}
case 'reduce':
return {count: state.count - 1}
default:
throw new Error()
}
}
let initialState = 0
const init = (initialState) => ({
count: initialState
})
// 第三個參數爲惰性初始化函數,能夠用來進行復雜計算返回最終的initialState,若是initialState較簡單能夠忽略此參數
const [state, dispatch] = useReducer(reducer, initialState, init)
return (
<div>
<p>{state.count}</p>
<button onClick={() => dispatch({type: 'add'})}>add</button>
<button onClick={() => dispatch({type: 'reduce'})}>reduce</button>
</div>
)
}
export default Parent
複製代碼
useContext能夠實現相似react-redux插件的功能,上層組件使用createContext建立一個context,並使用<MyContext.Provider>來傳遞context,下層組件使用useContext來接收context。
示例:
import React, { useState, createContext, useContext } from "react";
// 使用createContext來建立一個context
const CounterContext = createContext();
function Parent() {
const [count, setCount] = useState(0);
return (
// 父組件使用<MyContext.Provider>傳遞context
<CounterContext.Provider value={{ count, setCount }}>
{count}
<Child />
</CounterContext.Provider>
);
}
function Child() {
// 子組件使用useContext來接收context
const { count, setCount } = useContext(CounterContext);
return (
<div>
<button onClick={() => setCount(count + 1)}>add</button>
</div>
);
}
export default Parent;
複製代碼
其使用方法與useEffect同樣,但它會在全部的 DOM 變動以後同步調用 effect。可使用它來讀取 DOM 佈局並同步觸發重渲染。在瀏覽器執行繪製以前,useLayoutEffect 內部的更新計劃將被同步刷新。useEffect爲異步,useLayoutEffect爲同步,推薦你一開始先用 useEffect,只有當它出問題的時候再嘗試使用 useLayoutEffect
React Hooks中用來獲取DOM節點
示例:
import React, { useRef } from 'react'
function Parent() {
// 使用useRef建立一個ref,並在標籤中綁定到ref屬性上
const pRef = useRef(null)
return (
<div>
<p ref={pRef}>content</p>
</div>
)
}
export default Parent
複製代碼
自定義Hooks能夠實現邏輯複用等,在多個組件中能夠複用咱們自定義的Hooks,而且裏面的狀態是獨立的,自定義Hooks咱們通常按照規則以use開頭定義
示例:
import React, { useState } from "react";
// 自定義useCount的Hooks
function useCount() {
const [count, setCount] = useState(0);
return { count, setCount };
}
function Parent() {
// 父組件使用,狀態獨立
const { count, setCount } = useCount()
return (
<div>
<p>parent</p>
<p>{count}</p>
<button onClick={() => setCount(count + 1)}>add</button>
<Child />
</div>
);
}
function Child() {
// 子組件使用,狀態獨立
const { count, setCount } = useCount()
return (
<div>
<p>child</p>
<h1>{count}</h1>
<button onClick={() => setCount(count + 2)}>add</button>
</div>
);
}
export default Parent;
複製代碼
以上就是React Hooks的基本使用詳解,基本上能夠用React Hooks覆蓋咱們類組件的使用,官方也更推薦咱們使用React Hooks來開發新項目!