useMemo
我們知道當父組件發生重新渲染時,其所有(狀態、局部變量等)都是新的。一旦子組件依賴于父組件的某一個對象變量,那么無論對象是否發生變化,子組件拿到的都是新的對象,從而使子組件對應的 diff 失效,依舊會重新執行該部分邏輯。在下面的例子中,我們的副作用依賴項中包含了父組件傳入的對象參數,每次父組件發生更新時,都會觸發數據請求。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
function Info({ style, }) { console.log( 'Info 發生渲染' ); useEffect(() => { console.log( '重新加載數據' ); // 每次發生重新渲染時,都會重新加載數據 }, [style]); return ( <p style={style}> 這是 Info 里的文字 </p> ); } function Page() { console.log( 'Page 發生渲染' ); const [count, setCount] = useState(0); const style = { color: 'red' }; // 計數器 +1 時,引發 Page 的重新渲染,進而引發 Info 的重新渲染 return ( <div> <h4>計數值為:{count}</h4> <button onClick={() => setCount(count + 1)}> +1 </button> <Info style={style} /> </div> ); } |
React Hooks 給我們提供了解決方案,useMemo 允許我們緩存傳入的對象,僅當依賴項發生變化時,才重新計算并更新相應的對象。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
function Page() { console.log( 'Page 發生渲染' ); const [color] = useState( 'red' ); const [count, setCount] = useState(0); const style = useMemo(() => ({ color }), [color]); // 只有 color 發生實質性改變時,style 才會變化 // 計數器 +1 時,引發 Page 的重新渲染,進而引發 Info 的重新渲染 // 但是由于 style 緩存了,因此不會觸發 Info 內的數據重新加載 return ( <div> <h4>計數值為:{count}</h4> <button onClick={() => setCount(count + 1)}> +1 </button> <Info style={style} /> </div> ); } |
useCallback
React Hooks 在數據流上帶來的變化有兩點:一是支持更友好的使用 context 進行狀態管理,避免層級過多時向中間層承載無關參數;二是允許函數參與到數據流中,避免向下層組件傳入多余的參數。
useContext 作為 hooks 的核心模塊之一,可以獲取到傳入 context 的當前值,以此達到跨層通信的目的。React 官網有著詳細的介紹,需要關注的是一旦 context 值發生改變,所有使用了該 context 的組件都會重新渲染。為了避免無關的組件重繪,我們需要合理的構建 context ,比如從第一節提到的新思維模式出發,按狀態的相關度組織 context,將相關狀態存儲在同一個 context 中。
在過去,如果父子組件用到同一個數據請求方法 getData ,而該方法又依賴于上層傳入的 query 值時,通常需要將 query 和 getData 方法一起傳遞給子組件,子組件通過判斷 query 值來決定是否重新執行 getData。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
class Parent extends React.Component { state = { query: 'keyword' , } getData() { const url = `https: //mocks.alibaba-inc.com/mock/fO87jdfKqX/demo/queryData.json?query=${this.state.query}`; // 請求數據... console.log(`請求路徑為:${url}`); } render() { return ( // 傳遞了一個子組件不渲染的 query 值 <Child getData={ this .getData} query={ this .state.query} /> ); } } class Child extends React.Component { componentDidMount() { this .props.getData(); } componentDidUpdate(prevProps) { // if (prevProps.getData !== this.props.getData) { // 該條件始終為 true // this.props.getData(); // } if (prevProps.query !== this .props.query) { // 只能借助 query 值來做判斷 this .props.getData(); } } render() { return ( // ... ); } } |
在 React Hooks 中 useCallback 支持我們緩存某一函數,當且僅當依賴項發生變化時,才更新該函數。這使得我們可以在子組件中配合 useEffect ,實現按需加載。通過 hooks 的配合,使得函數不再僅僅是一個方法,而是可以作為一個值參與到應用的數據流中。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
function Parent() { const [count, setCount] = useState(0); const [query, setQuery] = useState( 'keyword' ); const getData = useCallback(() => { const url = `https: //mocks.alibaba-inc.com/mock/fO87jdfKqX/demo/queryData.json?query=${query}`; // 請求數據... console.log(`請求路徑為:${url}`); }, [query]); // 當且僅當 query 改變時 getData 才更新 // 計數值的變化并不會引起 Child 重新請求數據 return ( <> <h4>計數值為:{count}</h4> <button onClick={() => setCount(count + 1)}> +1 </button> <input onChange={(e) => {setQuery(e.target.value)}} /> <Child getData={getData} /> </> ); } function Child({ getData }) { useEffect(() => { getData(); }, [getData]); // 函數可以作為依賴項參與到數據流中 return ( // ... ); } |
以上就是React useMemo和useCallback的使用場景的詳細內容,更多關于React useMemo和useCallback的使用的資料請關注服務器之家其它相關文章!
原文鏈接:https://juejin.cn/post/6948605515068866590