Hook
是 React 16.8
中的新增功能。它們容許您在不編寫類的狀況下使用狀態和其餘 React
功能。HOOKS
只能在函數組件中使用react
React.memo
是一個高階的組件。它相似於 React.PureComponent
也就是說若是組件的 props
沒有改變,是不會被從新渲染的。git
function Foo (props) {
...
}
export default React.memo(Foo)
複製代碼
React.PureComponent
如何實現性能優化的咱們都知道類組件的中有個 shouldComponentUpdate
生命週期函數。當這個函數返回 false
時,表示組件不會從新渲染;當返回 true
時,表示組件會從新渲染。PureComponent
組件就是在 shouldComponentUpdate
函數中對組件接受的 props
進行比較(props
和 nextProps
進行比較)若是發生變化就返回 true
。同時還有自身的 state
數據也會進行一個比較(state
和 nextState
),若是發生變化就返回 true
。若是上面兩種數據的個自比較都返回 false,那麼組件就不會發生渲染,減小沒必要要的渲染,從而達到性能的優化。推薦一篇文章React PureComponent 源碼解析你們本身看看github
React.PureComponent
有什麼區別React.memo
爲高階組件。它與 React.PureComponent
很是類似,但它適用於函數組件,但不適用於 class
組件。若是你的函數組件接受了一個徹底相同的 props
那麼 memo
就不會使函數組件發生從新渲染。可是 memo
並不會對函數組件自身的 state
數據進行一個淺比較(useReducer
和 useState
鉤子返回的數據)。因此二者在做用上仍是有一些區別的。segmentfault
相似於類組件中的state,不一樣的是 useState
接受一個任意類型的值 string, array, object, bool...
做爲參數並返回一個數組,且 useState
只會在組件初始化的時候執行。數組
// 初始化的時候,age的值就是useState中參數的值
const [ age, setAge ] = useState(20);
const [ visible, setVisible ] = useState(props.visible);
複製代碼
數組中的第一個元素是狀態值,組件在運行過程當中會保留這個狀態值,相似於 this.state
數組中的第二個元素是改變這個狀體值的函數,相似於 this.setState()
性能優化
function Hooks(props) {
const [ age, setAge ] = useState(20);
const [ visible, setVisible ] = useState(props.visible);
return (
<div className=""> <p>個人年齡是{age}歲</p> <button onClick={() => setAge(age + 1)}>點擊</button> <p>{`${visible}`}</p> </div>
);
};
複製代碼
咱們能夠在函數組件中屢次使用 useState
,來建立多個狀態值供咱們使用。可是,必須在函數做用域的最頂層使用 useState
,不能嵌套在循環內部或者其餘函數做用域內部或者是塊級做用域中。app
對於使用過類組件的同窗來講,咱們能夠理解爲 useEffect
是類組件中 componentDidMount
和 componentDidUpdate
兩個生命週期的一個集合。每次當函數組件掛載成功或者從新渲染完成後都會調用 useEffect
。 可是也有不一樣的地方,useEffect
不徹底同類組件中的 componentDidMount
和 componentDidUpdate
生命週期函數同樣,useEffect
有延遲,在父組件 didMount 或 didUpdate 後,但在任何新渲染以前觸發。useEffect
能夠在組件中使用屢次, 和 useState
使用同樣。dom
useEffect
還能夠返回一個函數,並在組件即將銷燬時調用這個返回函數,沒錯,就是和類組件的 componentWillUnmount
同樣。咱們經過它來取消在 useEffect
中綁定的事件監聽等行爲。ide
通常狀況下,咱們能夠將一個行爲事件的綁定和取消綁定放在同一個 useEffect
中,這樣代碼的可讀性和維護行會更強一些。函數
function Hooks(props, ref) {
const boxRef = useRef(null);
useEffect(() => {
function handle() {
console.log(123456)
}
boxRef.current.addEventListener('click', handle, false);
return () => {
boxRef.current.removeEventListener('click', handle, false);
}
}, []);
return (
<div ref={boxRef}> 12344556 </div>
);
}
複製代碼
經過下面的這張圖,能夠看出來 useEffect
的有一個延遲
useEffect(() => {
document.title = `You clicked ${count} times`;
}, [count]); // 僅在 count 更改時更新
複製代碼
上面這個示例中,咱們傳入 [count]
做爲第二個參數。這個參數是什麼做用呢?若是 count
的值是 5,並且咱們的組件重渲染的時候 count
仍是等於 5,React
將對前一次渲染的 [5]
和後一次渲染的 [5]
進行比較。由於數組中的全部元素都是相等的(5 === 5),React 會跳過這個 effect
,這就實現了性能的優化。
當渲染時,若是 count
的值更新成了 6,React
將會把前一次渲染時的數組 [5] 和此次渲染的數組 [6] 中的元素進行對比。此次由於 5 !== 6,React
就會再次調用 effect
。若是數組中有多個元素,即便只有一個元素髮生變化,React
也會執行 effect
。
若是參數中有多個元素 [ age, props.visible ]
,組件渲染時經過比較後只要有一個元素髮生變化,useEffect
就會執行。若是參數是一個空數組 []
,那麼這個時候 useEffect
就和類組件中的 componentDidMount
同樣,只在組件掛載成功後調用一次。 useEffect
函數中 return
的函數,不受第二個參數的影響,仍在組件即將銷燬的時候調用。
其實,當第二個參數不是空的數組時,useEffect
也會在組件掛載成功後調用一次,這一點不能忘記。
不要在循環條件或嵌套函數中調用 Hook
。相反,始終在 React
函數的頂層使用 Hooks
。經過遵循此規則,您能夠確保每次組件呈現時都以相同的順序調用Hook
。這就是React
容許多個 useState
和 useEffect
調用之間正確保留 Hook
狀態的緣由。
和 useEffect
使用原理相同,可是惟一的區別在於 useLayoutEffect
不會延遲觸發,和類組件的 componentDidMount
和 componentDidUpdate
這兩個生命週期函數基本處於同步的狀態。
自定義 Hook
是一個 JavaScript
函數,其名稱以 "use" 開頭,能夠調用其餘 Hook
。構建本身的 Hook
能夠將組件邏輯提取到可重用的函數中 ,確保只在自定義 Hook
的頂層無條件地調用其餘 Hook
。與 React
組件不一樣,自定義 Hook
不須要具備特定簽名。咱們能夠決定它做爲參數須要什麼,以及它應該返回什麼(若是有的話)
// useVisibleStatus 是一個自定義的鉤子,咱們在函數中調用的useEffect
function useVisibleStatus(isShow) {
const [ visible, setVisible ] = useState(isShow);
useEffect(() => {
setVisible(isShow);
}, [ isShow ]);
return visible;
};
function Hooks(props) {
const [ count ] = useState(props.count);
const visible = useVisibleStatus(props.visible);
return (
<div className=""> <h2>{count}</h2> <button onClick={() => setCount(count + 1)}>點擊</button> <h2>{`${visible} ${props.count}`}</h2> </div>
);
}
複製代碼
咱們也能夠將一些複雜或者重複的邏輯提取提取到自定義的 Hook
函數中,從而簡化咱們的代碼。其實自定義 hook
和函數組件沒有多大區別。
當 useState
複雜的狀態邏輯涉及多個子值或下一個狀態取決於前一個狀態時,一般 useReducer
更可取。useReduce
還可讓您優化觸發深度更新的組件的性能
const initialState = {count: 0};
function reducer(state, action) {
switch (action.type) {
case 'increment':
return {count: state.count + 1};
case 'decrement':
return {count: state.count - 1};
default:
throw new Error();
}
}
function Counter({initialState}) {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<> Count: {state.count} <button onClick={() => dispatch({type: 'increment'})}>+</button> <button onClick={() => dispatch({type: 'decrement'})}>-</button> </> ); } 複製代碼
咱們看看 useReducer
具體的實現(自定義一個 useReducer
鉤子):
function useReducer(reducer, initialState) {
const [state, setState] = useState(initialState);
function dispatch(action) {
const nextState = reducer(state, action);
setState(nextState);
}
return [state, dispatch];
}
複製代碼
能夠經過 useImperativeHandle
,給ref上綁定一些自定的事件,前提是咱們必須聯合 forwardRef
一塊兒使用,注意全部的事件都是綁定在 ref
的 current
屬性上。 看下面的例子
// hook.js
function Hooks(props, ref) {
const [ count, setCount ] = useState(props.count);
useImperativeHandle(ref, () => ({
// 自定義一些事件
click: () => {
setCount(count + 1);
},
}));
return (
<div className=""> <h2>{count}</h2> <button onClick={() => setCount(count + 1)}>點擊</button> </div>
);
};
export default React.forwardRef(Hooks);
// Application.js
export default class App extends PureComponent {
componentDidMount() {
this.ref = React.createRef();
}
return (
<div onClick={() => this.ref.current.click()} > // ... <Hooks ref={this.ref} count={this.state.count} visible={this.state.visible}/> // ... </div> ); } 複製代碼
或者
function FancyInput(props, ref) {
// 獲取真是DOM節點
const inputRef = React.useRef();
useImperativeHandle(ref, () => ({
// 自定義一些事件
focus: () => {
// 在DOM節點執行一些操做均可以
inputRef.current.focus();
}
}));
return <input ref={inputRef} />; } FancyInput = React.forwardRef(FancyInput); 複製代碼
useRef
返回一個可變的ref對象,其 current
屬性值爲初始化傳遞的參數(initialValue)。返回的對象將持續整個組件的生命週期。和類組件中的實例屬性很像
const ref = usRef(20);
console.log(ref.current) // 20
// 能夠從新賦值
ref.current = 200;
複製代碼
固然最多見的就是訪問一個元素節點
function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
// `current` points to the mounted text input element
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}
複製代碼
使用的場景:函數組件中,咱們定義了一些方法,可是咱們並不但願每次組件更新的時候都從新執行一次個函數,那麼這個時候咱們就可使用 useMemo
。有個地方須要注意點那就是,useMemo
是在 useLayoutEffect
以前執行,這和類組件中的 componentWillMount
和 componentWillUpdate
相似。可是若是 useMemo
所接收的條件是 props
中的相關屬性的話,那麼咱們將 useMemo
看成類組件的 componentWillReceiveProps(nextProps)
使用,能夠查看的咱們demo
// 組件初始化的時候會調用 `Func`,相似 `componentWillMount``
// 當數組中的元素的值發生改變,那麼就會調用 `Func`,這個條件 a 和 b 有一個發生變化的時候 就會觸發 `useMemo`
useMemo(() => Func(a, b), [a, b]);
複製代碼
在看這個帶返回值的
function Hooks(props) {
const [ count, setCount ] = useState(props.count);
useLayoutEffect(() => {
console.log('useLayoutEffect 後執行');
setCount(props.count);
}, [ props.count ]);
const dom = useMemo(() => {
console.log('useMemo 優先執行');
return <h2>{count * 10}</h2>;
}, [count]);
return (
<div className=""> <h2>{count}</h2> <button onClick={() => setCount(count + 1)}>點擊</button> {dom} </div>
);
}
複製代碼
注意:傳入 useMemo
的函數會在渲染期間執行。請不要在這個函數內部執行與渲染無關的操做,諸如反作用這類的操做屬於 useEffec
的適用範疇,而不是 useMemo
useCallback
的使用和 useMemo
是同樣的,且 useCallback(fn, deps)
至關於 useMemo(() => fn, deps)
。
這是個人demo
const value = useContext(MyContext);
複製代碼
接收一個 context
對象(React.createContext
的返回值)並返回該 context
的當前值。當前的 context
值由上層組件中距離當前組件最近的 <MyContext.Provider>
的 value prop
決定。
當組件上層最近的 <MyContext.Provider>
更新時,該 Hook
會觸發重渲染,並使用最新傳遞給 MyContext provider
的 context value
值。
調用了 useContext
的組件總會在 context
值變化時從新渲染
const Thems = {
light: {
color: '#f90',
},
dack: {
color: '#222',
}
}
const ThemsContext = React.createContext({
them: Thems.light,
toggleTheme: () => {},
})
class Detail extends PureComponent {
state = {
context: {
them: Thems.light.color,
toggleTheme: this.handle,
}
}
handle = () => {
const { context: { them } } = this.state;
let color = Thems.light.color;
if (them === Thems.light.color) {
color = Thems.dack.color
}
this.setState({
context: {
...this.state.context,
them: color,
},
});
}
render() {
return (
<ThemsContext.Provider value={this.state.context}> <Hooks/> </ThemsContext.Provider> ); } } function Hooks(props, ref) { const themContext = React.useContext(ThemsContext); return ( <div style={{ background: themContext.them }} onClick={() => { themContext.toggleTheme() }} > 1234567890 </div> ); } 複製代碼
上面的這個 demo 中,當 ThemsContext.Provider
的 value props
發生變化時 Hooks
組件就會發生從新渲染。因此說這個時候咱們的目的就已經達到了。