注意:hooks只能在函數(無狀態組件)中使用html
useMome、useCallback、useEffect用法都差很少,都會在第一次渲染的時候執行,以後會在其依賴的變量發生改變時再次執行,而且這兩個hooks都返回緩存的值,useMemo返回緩存的變量,useCallback返回緩存的函數。react
const value = useMemo(fnM, [a]);
const fnA = useCallback(fnB, [a]);
複製代碼
React.memo 爲高階組件。它與React.PureComponent很是類似,但它適用於函數組件,但不適用於 class 組件。api
默認狀況下其只會對複雜對象作淺層對比,若是你想要控制對比過程,那麼請將自定義的比較函數經過第二個參數傳入來實現。這與shouldComponentUpdate 方法的返回值相反。數組
function MyComponent(props) {
/* 使用 props 渲染 */
}
function areEqual(prevProps, nextProps) {
/*
若是把 nextProps 傳入 render 方法的返回結果與
將 prevProps 傳入 render 方法的返回結果一致則返回 true,
不然返回 false
*/
}
export default React.memo(MyComponent, areEqual);
複製代碼
當父組件引入子組件的狀況下,每每會形成組件之間的一些沒必要要的浪費,下面咱們經過例子來了解一下場景緩存
const Child = (props) => {
console.log('子組件?')
return(
<div>我是一個子組件</div>
);
}
const Page = (props) => {
const [count, setCount] = useState(0);
return (
<>
<button onClick={(e) => { setCount(count+1) }}>加1</button>
<p>count:{count}</p>
<Child />
</>
)
}
複製代碼
每次父組件更新count,子組件都會更新。以下版本使用memo,count變化子組件沒有更新
const Child = (props) => {
console.log('子組件?')
return(
<div>我是一個子組件</div>
);
}
const ChildMemo = memo(Child);
const Page = (props) => {
const [count, setCount] = useState(0);
return (
<>
<button onClick={(e) => { setCount(count+1) }}>加1</button>
<p>count:{count}</p>
<ChildMemo />
</>
)
}
複製代碼
當父組件傳遞狀態給子組件的時候,memo好像沒什麼效果,子組件仍是執行了,這時候咱們就要引入hooks的useCallback、useMemo這兩個鉤子了。bash
//子組件會有沒必要要渲染的例子
interface ChildProps {
name: string;
onClick: Function;
}
const Child = ({ name, onClick}: ChildProps): JSX.Element => {
console.log('子組件?')
return(
<>
<div>我是一個子組件,父級傳過來的數據:{name}</div>
<button onClick={onClick.bind(null, '新的子組件name')}>改變name</button>
</>
);
}
const ChildMemo = memo(Child);
const Page = (props) => {
const [count, setCount] = useState(0);
const [name, setName] = useState('Child組件');
return (
<>
<button onClick={(e) => { setCount(count+1) }}>加1</button>
<p>count:{count}</p>
<ChildMemo name={name} onClick={(newName: string) => setName(newName)}/>
</>
)
}
複製代碼
在上面代碼基礎上,父級調用子級時,在onClick參數上加上useCallback,參數爲[],則第一次初始化結束後,不在改變。函數
//子組件沒有必要渲染的例子
interface ChildProps {
name: string;
onClick: Function;
}
const Child = ({ name, onClick}: ChildProps): JSX.Element => {
console.log('子組件?')
return(
<>
<div>我是一個子組件,父級傳過來的數據:{name}</div>
<button onClick={onClick.bind(null, '新的子組件name')}>改變name</button>
</>
);
}
const ChildMemo = memo(Child);
const Page = (props) => {
const [count, setCount] = useState(0);
const [name, setName] = useState('Child組件');
return (
<>
<button onClick={(e) => { setCount(count+1) }}>加1</button>
<p>count:{count}</p>
<ChildMemo name={name} onClick={ useCallback((newName: string) => setName(newName), []) }/>
{/* useCallback((newName: string) => setName(newName),[]) */}
{/* 這裏使用了useCallback優化了傳遞給子組件的函數,只初始化一次這個函數,下次不產生新的函數
</>
)
}
複製代碼
//子組件會有沒必要要渲染的例子
interface ChildProps {
name: { name: string; color: string };
onClick: Function;
}
const Child = ({ name, onClick}: ChildProps): JSX.Element => {
console.log('子組件?')
return(
<>
<div style={{ color: name.color }}>我是一個子組件,父級傳過來的數據:{name.name}</div>
<button onClick={onClick.bind(null, '新的子組件name')}>改變name</button>
</>
);
}
const ChildMemo = memo(Child);
const Page = (props) => {
const [count, setCount] = useState(0);
const [name, setName] = useState('Child組件');
return (
<>
<button onClick={(e) => { setCount(count+1) }}>加1</button>
<p>count:{count}</p>
<ChildMemo
name={{ name, color: name.indexOf('name') !== -1 ? 'red' : 'green'}}
onClick={ useCallback((newName: string) => setName(newName), []) }
/>
</>
)
}
複製代碼
更新屬性name爲對象類型,這時子組件仍是同樣的執行了,在父組件更新其它狀態的狀況下,子組件的name對象屬性會一直髮生從新渲染改變,從而致使一直執行,這也是沒必要要的性能浪費。性能
解決這個問題,使用name參數使用useMemo,依賴於State.name數據的變化進行更新優化
interface ChildProps {
name: { name: string; color: string };
onClick: Function;
}
const Child = ({ name, onClick}: ChildProps): JSX.Element => {
console.log('子組件?')
return(
<>
<div style={{ color: name.color }}>我是一個子組件,父級傳過來的數據:{name.name}</div>
<button onClick={onClick.bind(null, '新的子組件name')}>改變name</button>
</>
);
}
const ChildMemo = memo(Child);
const Page = (props) => {
const [count, setCount] = useState(0);
const [name, setName] = useState('Child組件');
return (
<>
<button onClick={(e) => { setCount(count+1) }}>加1</button>
<p>count:{count}</p>
<ChildMemo
//使用useMemo,返回一個和本來同樣的對象,第二個參數是依賴性,當name發生改變的時候,才產生一個新的對象
name={
useMemo(()=>({
name,
color: name.indexOf('name') !== -1 ? 'red' : 'green'
}), [name])
}
onClick={ useCallback((newName: string) => setName(newName), []) }
{/* useCallback((newName: string) => setName(newName),[]) */}
{/* 這裏使用了useCallback優化了傳遞給子組件的函數,只初始化一次這個函數,下次不產生新的函數
/>
</>
)
}
複製代碼
在子組件不須要父組件的值和函數的狀況下,只須要使用memo函數包裹子組件便可。而在使用值和函數的狀況,須要考慮有沒有函數傳遞給子組件使用useCallback,值有沒有所依賴的依賴項而使用useMemo,而不是盲目使用這些hooks等。ui