這篇文章兩個月以前寫的,看了一下官網文檔沒啥變化,就發出來了。若是有什麼錯誤,歡迎指出~html
前言:一直對這個新特性很是感興趣,終於今天有時間,花了大半天時間,把
Hooks
的官方教程過了一遍,收穫頗多,驚歎這個新特性真 TM 好用,之後開發用這個怕是要起飛了😆。react
const [state, setState] = useState(initialState);
複製代碼
useState
時,React
依賴於每次渲染時鉤子的調用順序都是同樣的(存在與每一個組件關聯的「存儲單元」的內部列表存放JavaScript對象),從而實現鉤子與狀態的一一對應關係。setState()
接收新的state
或者一個返回state
的函數(setCount(prevCount => prevCount - 1)}
)。setState
,useState
返回的setState
不會自動合併更新對象到舊的state
中(可使用useReducer
)。useState
能夠接收一個函數返回initialState
,它只會在初次渲染時被調用。setState
中的state
和當前的state
相等(經過Object.is
判斷),將會退出更新。const [rows, setRows] = useState(createRows(props.count)); // `createRows()`每次將會渲染將會被調用
複製代碼
優化一下:git
const [rows, setRows] = useState(() => createRows(props.count)); // `createRows()`只會被調用一次
複製代碼
其中的() => createRows(props.count)
會賦值給rows
,這樣就保證了只有在rows
調用時,纔會建立新的值。npm
useEffect(didUpdate);
複製代碼
componentDidMount
, componentDidUpdate
, componentWillUnmount
的組合。cleanup
)用於清理。cleanup phase
⏬useEffect(() => {
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
});
複製代碼
componentDidMount() {
ChatAPI.subscribeToFriendStatus(
this.props.friend.id,
this.handleStatusChange
);
}
// ====== 緣由在這裏 ======
componentDidUpdate(prevProps) {
// Unsubscribe from the previous friend.id
ChatAPI.unsubscribeFromFriendStatus(
prevProps.friend.id,
this.handleStatusChange
);
// Subscribe to the next friend.id
ChatAPI.subscribeToFriendStatus(
this.props.friend.id,
this.handleStatusChange
);
}
componentWillUnmount() {
ChatAPI.unsubscribeFromFriendStatus(
this.props.friend.id,
this.handleStatusChange
);
}
複製代碼
// Mount with { friend: { id: 100 } } props
ChatAPI.subscribeToFriendStatus(100, handleStatusChange); // Run first effect
// Update with { friend: { id: 200 } } props
ChatAPI.unsubscribeFromFriendStatus(100, handleStatusChange); // Clean up previous effect
ChatAPI.subscribeToFriendStatus(200, handleStatusChange); // Run next effect
// Update with { friend: { id: 300 } } props
ChatAPI.unsubscribeFromFriendStatus(200, handleStatusChange); // Clean up previous effect
ChatAPI.subscribeToFriendStatus(300, handleStatusChange); // Run next effect
// Unmount
ChatAPI.unsubscribeFromFriendStatus(300, handleStatusChange); // Clean up last effect
複製代碼
useEffect(() => {document.title = You clicked ${count} times;}, [count]);
,指定第二個參數(這裏爲[count
])變化時才發生cleanup phase
,而後執行effect
;useEffect
第二個參數爲爲[]
則表示只運行一次(componentDidMount
中執行effect
,componentWillUnmount
中進行cleanup
),永遠不從新運行。componentDidMount
/componentDidUpdate
有區別的地方在於,useEffect
中的函數會在layout
和paint
結束後才被觸發。(可使用useLayoutEffect
在下一次渲染以前(即 DOM 突變以後)同步觸發)useEffect
雖然被推遲到瀏覽器繪製完成以後,可是確定在有任何新的呈現以前啓動。由於React
老是在開始更新以前刷新以前渲染的效果。const context = useContext(Context);
複製代碼
接受一個上下文對象(由React.createContext
建立),返回當前上下文值(由最近的上下文提供)。數組
基本鉤子的變體或用於特定邊緣狀況的鉤子。瀏覽器
const [state, dispatch] = useReducer(reducer, initialArg, init);
複製代碼
init
爲函數,將會這樣調用:init(initialArg)
,返回初始值。state
和如今的state
同樣,將會在不影響子孫或者觸發效果的狀況下退出渲染。const memoizedCallback = useCallback(
() => {
doSomething(a, b);
},
[a, b],
);
複製代碼
傳入一個內聯回調和一個輸入數組,返回一個帶有記憶的函數,只有輸入數組中其中一個值變化纔會更改。useCallback(fn, inputs)
等價於 useMemo(() => fn, inputs)
。app
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
複製代碼
傳入一個建立函數和一個輸入數組,返回一個帶有記憶的值,只有輸入數組中其中一個值變化纔會從新計算。dom
const refContainer = useRef(initialValue);
// ...
<input ref={refContainer} />
...
複製代碼
返回一個可變的ref
對象,能夠自動將ref
對象中的current
屬性做爲初始值傳遞的參數,保持到組件的整個生命週期。函數
與在類中使用實例字段的方式相似,它能夠保留任何可變值。佈局
如保存前一個狀態:
function Counter() {
const [count, setCount] = useState(0);
const prevCountRef = useRef();
useEffect(() => {
prevCountRef.current = count;
});
const prevCount = prevCountRef.current;
return <h1>Now: {count}, before: {prevCount}</h1>;
}
複製代碼
useImperativeHandle(ref, createHandle, [inputs])
複製代碼
自定在使用 ref 時,公開給父組件的實例值,必須和forwardRef
一塊兒使用。
function FancyInput(props, ref) {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
}
}));
return <input ref={inputRef} ... />; } FancyInput = forwardRef(FancyInput); 複製代碼
<FancyInput ref={fancyInputRef} />
// 調用
fancyInputRef.current.focus()
複製代碼
使用方法和useLayoutEffect
一致,不過它是在 DOM 讀取佈局時同步觸發(至關於componentDidMount
和componentDidUpdate
階段)。(建議儘量使用useEffect
避免阻塞可視化更新)
useDebugValue(value)
複製代碼
用於在React DevTools
中顯示自定義鉤子的標籤,對於自定義鉤子中用於共享的部分有更大價值。
自定義顯示格式:
useDebugValue(date, date => date.toDateString());
複製代碼
正確作法:
useEffect(function persistForm() {
// 👍 We're not breaking the first rule anymore
if (name !== '') {
localStorage.setItem('formData', name);
}
});
複製代碼
React
函數組件中被調用。(能夠經過自定義鉤子函數解決)可使用eslint-plugin-react-hooks來強制自動執行這些規則。
use
開頭,一種公約。useState
和useEffect
是徹底獨立的。function Example() {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `You clicked ${count} times`;
});
return (
<div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div>
);
}
複製代碼
import React from 'react';
import ReactDOM from 'react-dom';
import { act } from 'react-dom/test-utils';
import Counter from './Counter';
let container;
beforeEach(() => {
container = document.createElement('div');
document.body.appendChild(container);
});
afterEach(() => {
document.body.removeChild(container);
container = null;
});
it('can render and update a counter', () => {
// Test first render and effect
act(() => {
ReactDOM.render(<Counter />, container); }); const button = container.querySelector('button'); const label = container.querySelector('p'); expect(label.textContent).toBe('You clicked 0 times'); expect(document.title).toBe('You clicked 0 times'); // Test second render and effect act(() => { button.dispatchEvent(new MouseEvent('click', {bubbles: true})); }); expect(label.textContent).toBe('You clicked 1 times'); expect(document.title).toBe('You clicked 1 times'); }); 複製代碼