中文字幕一区二区人妻电影,亚洲av无码一区二区乱子伦as ,亚洲精品无码永久在线观看,亚洲成aⅴ人片久青草影院按摩,亚洲黑人巨大videos

關(guān)于TypeScript的注釋:React Hooks

發(fā)布于:2021-02-05 13:55:20

0

338

0

React TypeScript Hooks React Hooks

導(dǎo)言

這些注釋應(yīng)該有助于更好地理解TypeScript,并且在需要查找如何在特定情況下利用TypeScript時(shí)可能會有所幫助。所有示例都基于TypeScript 3.2。

React Hooks

在“TypeScript注釋”系列的這一部分中,我們將了解如何使用TypeScript鍵入React Hooks,并在此過程中了解有關(guān)hooks的更多信息。

我們將參考官方React文檔中關(guān)于hooks的內(nèi)容,在需要了解更多關(guān)于hooks的信息或需要特定問題的具體答案時(shí),這是一個(gè)非常有價(jià)值的來源。

通常,在16.8中添加了hooks來React,使開發(fā)人員能夠在函數(shù)組件中使用狀態(tài),這在類組件中是唯一可能的。文檔說明有基本掛鉤和附加掛鉤。

基本掛鉤有useStateuseEffect、useContext,附加掛鉤包括useReducer、useCallbackuseMemo、useRef。

useState

讓我們從useState開始,這是一個(gè)基本的hooks,顧名思義,應(yīng)該用于狀態(tài)處理。

const [state, setState] = useState(initialState);

看看上面的例子,我們可以看到useState返回一個(gè)狀態(tài)值以及一個(gè)函數(shù)來更新它。但是我們?nèi)绾屋斎?/span>statesetState?
有趣的是,TypeScript可以推斷出類型,這意味著通過定義一個(gè)
initialState,可以推斷出狀態(tài)值和更新函數(shù)的類型。

const [state, setState] = useState(0);
// const state: number
const [state, setState] = useState("one");
// const state: string
const [state, setState] = useState({
 id: 1,
 name: "Test User"
});
/*
 const state: {
   id: number;
   name: string;
 }
*/
const [state, setState] = useState([1, 2, 3, 4]);
// const state: number[]

上面的例子很好地說明了我們不需要手工打字。但是如果我們沒有初始狀態(tài)呢?上述示例在嘗試更新狀態(tài)時(shí)會中斷。

我們可以在需要時(shí)使用手動(dòng)定義類型useState。

const [state, setState] = useState(null);
// const state: number | null
const [state, setState] = useState(null);
// const state: {id: number; name: string;} | null
const [state, setState] = useState(undefined);
// const state: number | null

可能還需要注意的是,與setState類內(nèi)組件相反,使用update hook函數(shù)需要返回完整的狀態(tài)。

const [state, setState] = useState({
 id: 1,
 name: "Test User"
});
/*
 const state: {
   id: number;
   name: string;
 }
*/

setState({name: "New Test User Name"}); // Error! Property 'id' is missing
setState(state => {
 return {...state, name: "New Test User Name"}
}); // Works!

另一件有趣的事情是,我們可以通過將函數(shù)傳遞給useState

const [state, setState] = useState(() => {
 props.init + 1;
});

// const state: number

同樣,TypeScript可以推斷狀態(tài)類型。

這意味著我們在使用useState時(shí)不需要做太多的工作,只有在沒有初始狀態(tài)的情況下,因?yàn)樵诔跏间秩緯r(shí)可能會計(jì)算實(shí)際的狀態(tài)形狀。

useEffect

另一個(gè)基本的hooks是useEffect,它在處理諸如日志記錄、突變或訂閱事件偵聽器之類的副作用時(shí)非常有用。一般來說,useEffect需要一個(gè)函數(shù)來運(yùn)行一個(gè)效果,該效果可以選擇性地返回一個(gè)clean-up函數(shù),該函數(shù)對于取消訂閱和刪除偵聽器非常有用。此外,useEffect還可以提供第二個(gè)參數(shù),該參數(shù)包含一個(gè)值數(shù)組,以確保僅當(dāng)其中一個(gè)值被刪除時(shí),效果函數(shù)才會運(yùn)行改變。這確保了我們可以控制何時(shí)運(yùn)行效果。

useEffect(
 () => {
   const subscription = props.source.subscribe();
   return () => {
     subscription.unsubscribe();
   };
 },
 [props.source]
);

以文檔中的原始示例為例,我們可以注意到在使用useEffect時(shí)不需要任何額外的鍵入。

TypeScript在嘗試返回非函數(shù)或effect函數(shù)中未定義的內(nèi)容時(shí)會抱怨。

useEffect(
 () => {
   subscribe();
   return null; // Error! Type 'null' is not assignable to void | (() => void)
 }
);

這也適用于useLayoutEffect,只在運(yùn)行效果時(shí)不同。

useContext

useContext需要一個(gè)上下文對象,并返回所提供上下文的值。當(dāng)提供者更新上下文時(shí),會觸發(fā)重新呈現(xiàn)??匆幌孪旅娴睦討?yīng)該會明白:

const ColorContext = React.createContext({ color: "green" });

const Welcome = () => {
 const { color } = useContext(ColorContext);
 returnWelcome;
};

同樣,我們不需要做太多關(guān)于類型的事情。類型是推斷出來的。

const ColorContext = React.createContext({ color: "green" });
const { color } = useContext(ColorContext);
// const color: string
const UserContext = React.createContext({ id: 1, name: "Test User" });
const { id, name } = useContext(UserContext);
// const id: number
// const name: string

useReducer

有時(shí)我們處理的是更復(fù)雜的狀態(tài),這可能也取決于前一個(gè)狀態(tài)。useReducer接受一個(gè)函數(shù),該函數(shù)根據(jù)先前的狀態(tài)和操作計(jì)算特定的狀態(tài)。以下示例摘自官方文檔。

const [state, dispatch] = useReducer(reducer, initialArg, init);

如果我們看一下文檔中的示例,就會注意到我們需要做一些額外的鍵入工作。檢查稍微調(diào)整的示例:

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 Counter({initialState = 0}) {
 const [state, dispatch] = useReducer(reducer, initialState);
 return (<>  Count: {state.count}dispatch({type: 'increment'})}>+dispatch({type: 'decrement'})}>-</>
 );
}

目前無法正確推斷。但是我們可以通過為reducer函數(shù)添加類型來改變這一點(diǎn)。通過在reducer函數(shù)中定義stateaction,我們現(xiàn)在可以推斷useReducer提供的state。讓我們改編一下這個(gè)例子。

type ActionType = {
 type: 'increment' | 'decrement';
};
type State = { count: number };
function reducer(state: State, action: ActionType) {
 // ...
}

現(xiàn)在我們可以確保在Counter中推斷類型:

function Counter({initialState = 0}) {
 const [state, dispatch] = useReducer(reducer, initialState);
 // const state = State
 // ...
}

當(dāng)試圖分派一個(gè)不存在的類型時(shí),我們將收到一個(gè)錯(cuò)誤。

dispatch({type: 'increment'}); // Works!
dispatch({type: 'reset'});
// Error! type '"reset"' is not assignable to type '"increment" | "decrement"'

useReducer也可以在需要時(shí)延遲初始化,因?yàn)橛袝r(shí)可能需要首先計(jì)算初始狀態(tài):

function init(initialCount) {
 return {count: initialCount};
}

function Counter({ initialCount = 0 }) {
 const [state, dispatch] = useReducer(red, initialCount, init);
 // const state: State
 // ...
}

從上面的示例中可以看出,由于正確鍵入了reducer函數(shù),因此使用延遲初始化的useReducer來推斷類型。

關(guān)于useReducer,我們不需要知道太多。

useCallback

有時(shí)我們需要回憶回叫。useCallback僅當(dāng)其中一個(gè)值發(fā)生更改時(shí),才接受一個(gè)內(nèi)聯(lián)回調(diào)和一個(gè)輸入數(shù)組來更新備忘錄。讓我們看一個(gè)例子:

const add = (a: number, b: number) => a + b;
const memoizedCallback = useCallback(
 (a) => {
   add(a, b);
 },
 [b]
);

有趣的是,我們可以用任何類型調(diào)用memoizedCallback,而不會看到TypeScript抱怨:

memoizedCallback("ok!"); // Works!
memoizedCallback(1); // Works!

在這種特定情況下,memoizedCallback可以處理字符串或數(shù)字,盡管add函數(shù)需要兩個(gè)數(shù)字。要解決這個(gè)問題,我們需要在編寫內(nèi)聯(lián)函數(shù)時(shí)更加具體。

const memoizedCallback = useCallback(
 (a: number) => {
   add(a, b);
 },
 [b]
);

現(xiàn)在,我們需要傳遞一個(gè)數(shù)字,否則編譯器會投訴。

memoizedCallback("ok");
// Error! Argument of type '"ok"' is not assignable to argument of type 'number'
memoizedCallback(1); // Works!

useMemo

useMemouseCallback非常相似,但返回的是已記憶的值,而不是已記憶的回調(diào)。以下內(nèi)容摘自文檔。

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

因此,如果我們在上面的基礎(chǔ)上構(gòu)建一個(gè)示例,我們注意到不需要對類型做任何事情:

function calculate(a: number): number {
 // do some calculations here...
}

function runCalculate() {
 const calculatedValue =  useMemo(() => calculate(a), [a]);
 // const calculatedValue : number
}

useRef

最后,我們再看一個(gè)hooks:useRef

當(dāng)使用useRef時(shí),我們可以訪問可變的引用對象。此外,我們可以將初始值傳遞給useRef,該值用于初始化可變r(jià)ef對象公開的current屬性。這在嘗試訪問函數(shù)f.e.中的某些組件時(shí)非常有用。同樣,讓我們使用文檔中的示例。

function TextInputWithFocusButton() {
 const inputEl = useRef(null);
 const onButtonClick = () => {
   // `current` points to the mounted text input element
   inputEl.current.focus(); // Error! Object is possibly 'null'
 };
 return (<>Focus the input</>
 );
}

我們可以看到TypeScript在抱怨,因?yàn)槲覀冇?/span>null初始化了useRef,這是一個(gè)有效的情況,因?yàn)橛袝r(shí)設(shè)置引用可能發(fā)生在稍后的時(shí)間點(diǎn)。
這意味著,在使用
useRef時(shí),我們需要更加明確。

function TextInputWithFocusButton() {
 const inputEl = useRef(null);
 const onButtonClick = () => {
   inputEl.current.focus(); // Error! Object is possibly 'null'
 };
 // ...
}

當(dāng)通過定義實(shí)際類型來使用useRef時(shí),更加具體化仍然不能消除錯(cuò)誤。實(shí)際檢查current屬性是否存在,可以防止編譯器抱怨。

function TextInputWithFocusButton() {
 const inputEl = useRef(null);
 const onButtonClick = () => {
   if (inputEl.current) {
     inputEl.current.focus(); // Works!
   }
 };
 // ...
}

useRef也可用作實(shí)例變量。

如果需要更新current屬性,則需要將useRef與泛型類型Type | null一起使用:

function sleep() {
 const timeoutRefId = useRef();

 useEffect(() => {
   const id = setTimeout(() => {
     // ...
   });
   if (timeoutRefId.current) {
     timeoutRefId.current = id;
   }
   return () => {
     if (timeoutRefId.current) {
       clearTimeout(timeoutRefId.current);
     }
   };
 });
 // ...
}

關(guān)于React hooks,還有一些更有趣的東西需要學(xué)習(xí),但不是特定于TypeScript的。