一、useReducer
管理相對復雜的狀態數據
定義一個reducer函數,根據action值的不同返回不同的狀態
在組件中調用useReducer并傳入reducer函數和狀態的初始值
事件發生時,通過dispatch函數分派一個對象,即通知reducer具體返回哪個狀態對應的操作
import { useReducer } from "react";function reducer(state, action){switch(action.type){case 'INC':return state + 1case 'DEC':return state - 1case 'SET':return action.payloaddefault:return state}
}
function App() {const [state, dispatch] = useReducer(reducer, 0)return (<div className="App"><button onClick={()=>dispatch({type:'DEC'})}>-1</button>{state}<button onClick={()=>dispatch({type:'INC'})}>+1</button>{/* 自定義參數 */}<button onClick={()=>dispatch({type:'SET', payload:100})}>set 100</button></div>);
}export default App;
?二、useMemo
組件重新渲染時緩存計算的結果
應用場景:消耗比較大的計算
import { useMemo, useState } from "react"
function fib(num){console.log('計算函數執行了')if ( num < 3 )return 1return fib(num - 1) + fib(num - 2)}
function App() {const [count1, setCount1] = useState(0)const [count2, setCount2] = useState(0)// 如果執行下面注釋的代碼 即使只有count2變化 計算函數也會執行// const result = fib(count1)const result = useMemo(() => {return fib(count1)},[count1])console.log('組件重新渲染')return (<div className="App"><button onClick={()=> setCount1(count1 + 1)}>count1:{count1}</button><button onClick={()=> setCount2(count2 + 1)}>count2:{count2}</button>{result}</div>);
}export default App;
三、React.memo
react組件默認的渲染機制:只要父組件重新渲染子組件就會重新渲染
用法
const MemoComponent = memo(function SomeComponent(props){// ...
})
只有props發生變化時,memo包裹的緩存組件才會重新渲染
import { useState, memo } from "react";
const MemoSon = memo(function Son(props){console.log('子組件渲染了')return <div>Son</div>
})
function App() {const [count, setCount] = useState(0)console.log('父組件渲染了')return (<div className="App"><button onClick={() => setCount(count+1)}>+1</button><MemoSon/></div>);
}export default App;
props的比較機制
使用memo緩存組件之后,react會對每一個prop使用Object.is比較新值和老值,返回true表示沒有變化
1. 傳遞一個簡單類型的prop prop變化時組件重新渲染
2. 傳遞一個引用類型的prop 比較的是新值和舊值的引用是否相等 當父組件的函數重新執行時 實際上形成的是新的數組引用
3. 保證引用穩定 -> useMemo 組件渲染的過程中緩存一個值
//情況1 傳遞簡單數據
//點擊按鈕 子組件會發生變化
import { useState, memo } from "react";
const MemoSon = memo(function Son(props){console.log('子組件渲染了')return <div>Son{props.count}</div>
})
function App() {const [count, setCount] = useState(0)console.log('父組件渲染了')return (<div className="App"><button onClick={() => setCount(count+1)}>+1</button><MemoSon count={count}/></div>);
}export default App;
//情況2 傳遞引用數據
//點擊按鈕 子組件會發生變化
import { useState, memo } from "react";
const MemoSon = memo(function Son(props){console.log('子組件渲染了')return <div>Son{props.list}</div>
})
function App() {const [count, setCount] = useState(0)console.log('父組件渲染了')const list = [1,2,3]return (<div className="App"><button onClick={() => setCount(count+1)}>+1</button><MemoSon list={list}/></div>);
}export default App;
//情況3 使用useMemo進行緩存
//子組件不再重新渲染
import { useState, memo, useMemo } from "react";
const MemoSon = memo(function Son(props){console.log('子組件渲染了')return <div>Son{props.list}</div>
})
function App() {const [count, setCount] = useState(0)console.log('父組件渲染了')const list = useMemo(()=>{return [1,2,3]}, [])return (<div className="App"><button onClick={() => setCount(count+1)}>+1</button><MemoSon list={list}/></div>);
}export default App;
useCallback
4. 給子組件傳遞函數
React.memo檢測的是props中數據的棧地址是否改變。而父組件重新構建的時候,會重新構建父組件中的所有函數(舊函數銷毀,新函數創建,等于更新了函數地址),新的函數地址傳入到子組件中被props檢測到棧地址更新。也就引發了子組件的重新渲染。
在組件多次渲染的時候緩存函數
使用場景:在往子組件傳入了一個函數并且子組件被React.momo緩存了的時候使用
//情況4 傳入函數
//點擊按鈕子組件不會重新渲染 因為使用useCallback緩存
import { useState, memo, useMemo, useCallback } from "react";
const Input = memo(function Input({onChange}){console.log('子組件重新渲染')return <input type="text" onChange={(e) => onChange(e.target.value)} />
})
function App() {const [count, setCount] = useState(0)const changeHandler = useCallback((value)=> console.log(value), [])return (<div className="App"><Input onChange={changeHandler}/><button onClick={() => setCount(count+1)}>{count}</button></div>);
}export default App;
四、React.forwardRef
import { useRef,forwardRef } from "react";//如果用下面的函數 sonRef的值是null
// function Son(){
// return <input type="text"/>
// }
const Son = forwardRef((props, ref)=> {return <input type="text" ref={ref}/>
})
function App() {const sonRef = useRef(null)const showRef = ()=>{console.log(sonRef)}return (<div className="App"><Son ref={sonRef}/><button onClick={showRef}>focus</button></div>);
}export default App;