這是官網的解釋:_Hook_ 是 React 16.8 的新增特性。它可讓你在不編寫 class 的狀況下使用 state 以及其餘的 React 特性。html
阮一峯大神的解釋:React Hooks 的意思是,組件儘可能寫成純函數,若是須要外部功能和反作用,就用鉤子把外部代碼"鉤"進來。node
個人理解:Hook是能夠提供給咱們更加簡潔的方式去使用React的其餘特性。react
可使用 Hook 從組件中提取狀態邏輯,使得這些邏輯能夠單獨測試並複用。Hook 使你在無需修改組件結構的狀況下複用狀態邏輯。 這使得在組件間或社區內共享 Hook 變得更便捷。編程
咱們常常維護一些組件,組件起初很簡單,可是逐漸會被狀態邏輯和反作用充斥。爲了解決這個問題,Hook 將組件中相互關聯的部分拆分紅更小的函數(好比設置訂閱或請求數據),而並不是強制按照生命週期劃分。你還可使用 reducer 來管理組件的內部狀態,使其更加可預測。json
Hook 使你在非 class 的狀況下可使用更多的 React 特性。 從概念上講,React 組件一直更像是函數。而 Hook 則擁抱了函數,同時也沒有犧牲 React 的精神原則。Hook 提供了問題的解決方案,無需學習複雜的函數式或響應式編程技術。api
const [state, setState] = useState(initialState);
返回值:state,更新state的函數setState數組
setState 函數用於更新 state。它接收一個新的 state 值並將組件的一次從新渲染加入隊列。setState(newState);瀏覽器
React 會確保 setState 函數的標識是穩定的,而且不會在組件從新渲染時發生變化。性能優化
參數:初始的state:initialStatedom
在初始渲染期間,返回的狀態 (state) 與傳入的第一個參數 (initialState) 值相同。
示例代碼:
function Counter({initialCount}) { const [count, setCount] = useState(initialCount); return ( <> Count: {count} <button onClick={() => setCount(initialCount)}>Reset</button> <button onClick={() => setCount(prevCount => prevCount - 1)}>-</button> <button onClick={() => setCount(prevCount => prevCount + 1)}>+</button> </> ); }
注意:與 class 組件中的 setState 方法不一樣,useState 不會自動合併更新對象。你能夠用函數式的 setState 結合展開運算符來達到合併更新對象的效果。
setState(prevState => { // 也可使用 Object.assign return {...prevState, ...updatedValues}; });
該Hook接收一個包含命令式、且有可能有反作用代碼的函數。
useEffect(didUpdate); //didUpdate指的是包含命令式、且有可能有反作用代碼的函數
在函數組件主體內(這裏指在 React 渲染階段)改變 DOM、添加訂閱、設置定時器、記錄日誌以及執行其餘包含反作用的操做都是不被容許的,由於這可能會產生莫名其妙的 bug 並破壞 UI 的一致性。
使用 useEffect 完成反作用操做。賦值給 useEffect 的函數會在組件渲染到屏幕以後執行。你能夠把 effect 看做從 React 的純函數式世界通往命令式世界的逃生通道。
默認狀況下,effect 將在每輪渲染結束後執行,但你能夠選擇讓它 在只有某些值改變的時候 才執行。
[](https://zh-hans.reactjs.org/d...,組件卸載時須要清除 effect 建立的諸如訂閱或計時器 ID 等資源。要實現這一點,useEffect 函數需返回一個清除函數。如下就是一個建立訂閱的例子:
useEffect(() => { const subscription = props.source.subscribe(); return () => { // 清除訂閱 subscription.unsubscribe(); }; });
默認狀況下,effect 會在每輪組件渲染完成後執行。這樣的話,一旦 effect 的依賴發生變化,它就會被從新建立。
然而,在某些場景下這麼作可能會矯枉過正。好比,在上面的例子中,咱們不須要在每次組件更新時都建立新的訂閱,而是僅須要在 source prop 改變時從新建立。
要實現這一點,能夠給 useEffect 傳遞第二個參數,它是 effect 所依賴的值數組。更新後的示例以下:
useEffect( () => { const subscription = props.source.subscribe(); return () => { subscription.unsubscribe(); }; }, [props.source], //第二個參數是Effect的依賴項,在這裏,只有當該數組發生變化的時候,纔去執行useEffect );
此時,只有當 props.source 改變後纔會從新建立訂閱。
const value = useContext(MyContext); //MYContext是建立好的context對象
注意:
1.useContext(MyContext) 只是讓你可以_讀取_ context 的值以及訂閱 context 的變化。你仍然須要在上層組件樹中使用 <MyContext.Provider> 來爲下層組件_提供_ context。
示例代碼:
const themes = { light: { foreground: "#000000", background: "#eeeeee" }, dark: { foreground: "#ffffff", background: "#222222" } }; const ThemeContext = React.createContext(themes.light); function App() { return ( <ThemeContext.Provider value={themes.dark}> <Toolbar /> </ThemeContext.Provider> ); } function Toolbar(props) { return ( <div> <ThemedButton /> </div> ); } function ThemedButton() { const theme = useContext(ThemeContext); //這裏訂閱到了provider傳遞下來的value值 return ( <button style={{ background: theme.background, color: theme.foreground }}> I am styled by theme context! </button> ); }
const [state, dispatch] = useReducer(reducer, initialArg, init);
它接收一個形如 (state, action) => newState 的 reducer,並返回當前的 state 以及與其配套的 dispatch 方法。
useReducer和useState的比較:這二者都是返回了新的狀態和一個函數。在某些場景下,useReducer 會比 useState 更適用,例如 state 邏輯較複雜且包含多個子值,或者下一個 state 依賴於以前的 state 等。而且,使用 useReducer 還能給那些會觸發深更新的組件作性能優化。
如下是用 reducer 重寫 useState 一節的計數器示例:
const initialState = {count: 0}; function reducer(state, action) { //reducer switch (action.type) { case 'increment': return {count: state.count + 1}; case 'decrement': return {count: state.count - 1}; default: throw new Error(); } } function Counter() { const [state, dispatch] = useReducer(reducer, initialState); return ( <> Count: {state.count} <button onClick={() => dispatch({type: 'decrement'})}>-</button> <button onClick={() => dispatch({type: 'increment'})}>+</button> </> ); }
第三個參數:init
你能夠選擇惰性地建立初始 state。爲此,須要將 init 函數做爲 useReducer 的第三個參數傳入,這樣初始 state 將被設置爲 init(initialArg)。
仍是上一個計數器的代碼,這裏使用了第三個參數添加了一個reset功能:
function init(initialCount) { //init函數:用於惰性建立初始state return {count: initialCount}; } function reducer(state, action) { switch (action.type) { case 'increment': return {count: state.count + 1}; case 'decrement': return {count: state.count - 1}; case 'reset': return init(action.payload); default: throw new Error(); } } function Counter({initialCount}) { const [state, dispatch] = useReducer(reducer, initialCount, init); //init函數做爲第三個參數傳入useReducer return ( <> Count: {state.count} <button onClick={() => dispatch({type: 'reset', payload: initialCount})}> Reset </button> <button onClick={() => dispatch({type: 'decrement'})}>-</button> <button onClick={() => dispatch({type: 'increment'})}>+</button> </> ); }
const refContainer = useRef(initialValue);
useRef 返回一個可變的 ref 對象,其 .current 屬性被初始化爲傳入的參數(initialValue)。返回的 ref 對象在組件的整個生命週期內保持不變。
一個常見的用例即是命令式地訪問子組件:
function TextInputWithFocusButton() { const inputEl = useRef(null); //建立一個ref對象 const onButtonClick = () => { // `current` 指向已掛載到 DOM 上的文本輸入元素 inputEl.current.focus(); }; return ( <> <input ref={inputEl} type="text" /> //將建立好的ref對象放進ref屬性中 <button onClick={onButtonClick}>Focus the input</button> </> ); }
useRef和ref屬性的區別:
注意:
當 ref 對象內容發生變化時,useRef 並_不會_通知你。變動 .current 屬性不會引起組件從新渲染。若是想要在 React 綁定或解綁 DOM 節點的 ref 時運行某些代碼,則須要使用回調 ref 來實現。
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
注意點:
與useEffect的區別:
在計算機科學中,函數反作用指當調用函數時,除了返回函數值以外,還對主調用函數產生附加的影響。例如修改全局變量(函數外的變量)或修改參數。
對於運行在瀏覽器的JS來講,反作用包括且不限於:
上例的Hooks代碼還能夠封裝起來,變成一個自定義的Hook,便於共享。
const usePerson = (personId) => { const [loading, setLoading] = useState(true); const [person, setPerson] = useState({}); useEffect(() => { setLoading(true); fetch(`https://swapi.co/api/people/${personId}/`) .then(response => response.json()) .then(data => { setPerson(data); setLoading(false); }); }, [personId]); return [loading, person]; };
上面代碼中,usePerson()就是一個自定義的Hook。
Person組件就改用這個新的鉤子,引入封裝的邏輯。
const Person = ({ personId }) => { const [loading, person] = usePerson(personId); if (loading === true) { return <p>Loading ...</p>; } return ( <div> <p>You're viewing: {person.name}</p> <p>Height: {person.height}</p> <p>Mass: {person.mass}</p> </div> ); };
本文未完持續更新中...