React hook && 函數組件相關
Hook 是 React 16.8 的新增特性,函數組件每次調用其生產的hook類型、順序、數量應該都是一致的。不然會報錯uncaught Invariant Violation: Rendered more/less hooks than during the previous render.
1.useState
import { useState } from 'react';
const [columns, setColumns] = useState([]);
2.useEffect
如果熟悉 React 類聲明週期方法,可以把 useEffect Hook 視作 componentDidMount、componentDidUpdate 和 componentWillUnmount 的組合體
與 componentDidMount 和 componentDidUpdate 不同,使用 useEffect 調度的副作用不會阻塞瀏覽器更新屏幕。這使得 application 感覺上具有響應式。大多數副作用不需要同步發生。而如果需要同步進行,(比如測量佈局),有一個單獨的 useLayoutEffect Hook, API 和 useEffect 相同。
從 effect 中返回一個 function?
這是 effect 可選的清理機制。每個 effect 都可以返回一個在它之後清理的 function。這使得我們能夠保持添加訂閱和刪除訂閱彼此接近的訂閱的邏輯。這同樣是 effect 的一部分。
在某些情況下,每次 render 後清理或者使用 effect 可能會產生性能問題。在類組件中,可以通過 componentDidUpdate 中編寫 prevProps 或 prevState 的額外比較來解決這個問題:
componentDidUpdate(prevProps, prevState) {
if (prevState.count !== this.state.count) {
document.title = `You clicked ${this.state.count} times`;
}
}
這個要求很常見,而這種方式已經被內置到 useEffect Hook 的 API中,如果在重新渲染之間沒有更新某些值,則可以告訴 React 跳過 effect,爲了實現這種方式,需要將數組作爲可選的第二個參數傳遞給 useEffect:
import { useEffect } from 'react';
useEffect(() => {
document.title = `You clicked ${count} times`;
}, [count]);// 只有在 count 發生變化的時候纔會執行這個 effect
3.useRequest
const { data: latestPipelineInstanceAndQualityReportData } = useRequest(
async () => {
const instance = await pipelineInstanceApi.getLatestByBranch(branchType, Number(branchId));
let imageQuality = null;
if (instance) {
imageQuality = await pipelineInstanceApi.getImageQuality(instance.id);
}
return { instance, imageQuality };
},
{
refreshDeps: [branchId],
},
);
以及
const { data: monitorPageConfList, loading } = useRequest(
async () => {
return monitorApi.getMonitorPageConf();
},
{
refreshDeps: [],
onError: (error) => {
message.error(`獲取監控配置失敗:${error.message}`);
}
},
);
if (loading) {
4.useContext
新建一箇中間文件context.tx
,內容爲:
export const codeReviewContext = React.createContext({});
父組件這麼寫:
import { codeReviewContext } from './context';
尖括號codeReviewContext.Provider
value={{ branchId, branchType, demandId, tab, gitUrl, uri, query, isReviewer, thumbsUped }}
>
尖括號子組件/>
尖括號/codeReviewContext.Provider>
5.useCallback
使用場景是:有一個父組件,其中包含子組件,子組件接收一個函數作爲props;通常而言,如果父組件更新了,子組件也會執行更新;但是大多數場景下,更新是沒有必要的,我們可以藉助useCallback來返回函數,然後把這個函數作爲props傳遞給子組件;這樣,子組件就能避免不必要的更新。
import React, { useCallback } from 'react';
const getReleaseLog = useCallback(async (service_name) => {
if (!service_name) {
return Promise.resolve([]);
}
try {
const diff_list = await serviceApi.diffRequirement(service_name);
return Promise.resolve(diff_list || []);
} catch (err) {
message.error('getChangeLog Error: ' + err.message, 3);
console.error('---fetch ChangelogContent error: ', err.message);
return Promise.resolve([]);
}
}, []);
6.useMemo
import { useMemo } from 'react';
function Button({ name, children }) {
function changeName(name) {
console.log('11')
return name + '改變name的方法'
}
const otherName = useMemo(()=>changeName(name),[name])
return (
<>
<div>{otherName}</div>
<div>{children}</div>
</>
)
}
export default Button
這個時候我們點擊 改變content值的按鈕,發現changeName 並沒有被調用。
但是點擊改變name值按鈕的時候,changeName被調用了。
所以我們可以使用useMemo方法 避免無用方法的調用,當然一般我們changName裏面可能會使用useState來改變state的值,那是不是就避免了組件的二次渲染。
達到了優化性能的目的
7.useRef
一個常見的用例是強制訪問子組件:
import { useRef } from 'react';
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>
</>
);
}
另一個例子是異步回調函數裏面要獲取某個變量的最新值而不是當時的快照值
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
function handleAlertClick() {
setTimeout(() => {
alert("You clicked on: " + count);
}, 3000);
}
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
<button onClick={handleAlertClick}>Show alert</button>
</div>
);
}
改寫爲:
function Example() {
const [count, setCount] = useState(0);
const latestCount = useRef(count);
useEffect(() => {
// Set the mutable latest value
latestCount.current = count;
setTimeout(() => {
// Read the mutable latest value
console.log(`You clicked ${latestCount.current} times`);
}, 3000);
});
// ...
}
import React, { useState, useRef } from 'react';
function Counter() {
const [count, setCount] = useState(0);
const latestCount = useRef(count);
useEffect(() => {
latestCount.current = count;
});
function handleAlertClick() {
setTimeout(() => {
alert("You clicked on: " + latestCount.current);
}, 3000);
}
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
<button onClick={handleAlertClick}>Show alert</button>
</div>
);
}
7.useLayoutEffect
用在處理DOM的時候,當你的useEffect裏面的操作需要處理DOM,並且會改變頁面的樣式,就需要用這個,否則可能會出現出現閃屏問題, useLayoutEffect裏面的callback函數會在DOM更新完成後立即執行,但是會在瀏覽器進行任何繪製之前運行完成,阻塞了瀏覽器的繪製.
8.react.memo
引入於16.6.0
react.memo的實現很簡單,就幾行代碼:
export default function memo<Props>(
type: React$ElementType,
compare?: (oldProps: Props, newProps: Props) => boolean,
) {
if (__DEV__) {
if (!isValidElementType(type)) {
warningWithoutStack(
false,
'memo: The first argument must be a component. Instead ' +
'received: %s',
type === null ? 'null' : typeof type,
);
}
}
return {
$$typeof: REACT_MEMO_TYPE,
type,
compare: compare === undefined ? null : compare,
};
}