React 源碼閱讀-8
ReactHooks
Hook
是 React 16.8
的新增特性。它可以讓你在不編寫 class
的情況下使用 state
以及其他的 React
特性。
import {
useCallback,
useContext,
useEffect,
useImperativeHandle,
useDebugValue,
useLayoutEffect,
useMemo,
useReducer,
useRef,
useState,
useResponder,
} from './ReactHooks';
useState
useState
會返回一對值:當前狀態和一個讓你更新它的函數,你可以在事件處理函數中或其他一些地方調用這個函數
它類似 class
組件的 this.setState
,但是它不會把新的 state
和舊的 state
進行合併;
import React, { useState } from "react";
import "./App.css";
function App() {
const [count, setCount] = useState(0);
return (
<div className="App">
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
</div>
);
}
export default App;
useEffect
import React, { useState, useEffect } from "react";
import "./App.css";
function App() {
const [count, setCount] = useState(0);
useEffect(() => {
// Update the document title using the browser API
document.title = `You clicked ${count} times`;
});
return (
<div className="App">
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
</div>
);
}
export default App;
相當於 React class
的生命週期函數,你可以把 useEffect Hook
看做 componentDidMount
,componentDidUpdate
和 componentWillUnmount
這三個函數的組合.
默認情況下,effect
會在每輪組件渲染完成後執行。這樣的話,一旦effect
的依賴發生變化,它就會被重新創建.
可以給 useEffect
傳遞第二個參數,它是 effect 所依賴的值數組。更新後的示例如下:
useEffect(
() => {
const subscription = props.source.subscribe();
return () => {
subscription.unsubscribe();
};
},
[props.source],
);
如果想執行只運行一次的 effect
(僅在組件掛載和卸載時執行),可以傳遞一個空數組([]
)作爲第二個參數。這就告訴 React
你的 effect
不依賴於 props
或 state
中的任何值,所以它永遠都不需要重複執行。這並不屬於特殊情況 —— 它依然遵循輸入數組的工作方式。
useContext
類似之前提到過的上下文 Context
const value = useContext(MyContext);
接收一個 context
對象(React.createContext
的返回值)並返回該 context
的當前值。當前的 context
值由上層組件中距離當前組件最近的 <MyContext.Provider>
的 value prop
決定
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);
return (
<button style={{ background: theme.background, color: theme.foreground }}>
I am styled by theme context!
</button>
);
}
useContext
的參數必須是 context
對象本身
useReducer
const [state, dispatch] = useReducer(reducer, initialArg, init);
接收一個形如 (state, action) => newState
的 reducer
,並返回當前的 state
以及與其配套的 dispatch
方法
和 redux
比較類似.
實現的計數器
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 App() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<>
Count: {state.count}
<button onClick={() => dispatch({ type: "decrement" })}>-</button>
<button onClick={() => dispatch({ type: "increment" })}>+</button>
</>
);
}
指定初始的state
const [state, dispatch] = useReducer(
reducer,
{count: initialCount}
);
惰性初始化
你可以選擇惰性地創建初始 state
。爲此,需要將 init
函數作爲 useReducer
的第三個參數傳入,這樣初始 state
將被設置爲 init(initialArg)
。
function init(initialCount) {
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 App({initialCount}) {
const [state, dispatch] = useReducer(reducer, initialCount, init);
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>
</>
);
}
跳過 dispatch
如果 Reducer Hook
的返回值與當前 state
相同,React
將跳過子組件的渲染及副作用的執行
React
使用 Object.is
比較算法 來比較 state
useCallback
把內聯回調函數及依賴項數組作爲參數傳入 useCallback
,它將返回該回調函數的 memoized
版本,該回調函數僅在某個依賴項改變時纔會更新。當你把回調函數傳遞給經過優化的並使用引用相等性去避免非必要渲染(例如 shouldComponentUpdate
)的子組件時,它將非常有用。
const memoizedCallback = useCallback(
() => {
doSomething(a, b);
},
[a, b],
);
useRef
useRef
返回一個可變的 ref
對象,其 .current
屬性被初始化爲傳入的參數(initialValue
)。返回的 ref
對象在組件的整個生命週期內保持不變。
和之前的 ref
,forwardref
,createref
類似,只不過這個是在函數組件裏的
function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
// `current` 指向已掛載到 DOM 上的文本輸入元素
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}
useMemo
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
把“創建”函數和依賴項數組作爲參數傳入 useMemo,它僅會在某個依賴項改變時才重新計算 memoized
值。這種優化有助於避免在每次渲染時都進行高開銷的計算。
類似memo
其他 API
不展開敘述了 感覺都是差不多的
https://zh-hans.reactjs.org/d...