1. 程式人生 > 程式設計 >TypeScript在React專案中的使用實踐總結

TypeScript在React專案中的使用實踐總結

序言

本文會側重於TypeScript(以下簡稱TS)在專案中與React的結合使用情況,而非TS的基本概念。關於TS的型別檢視可以使用線上TS工具👉TypeScript遊樂場

React元素相關

React元素相關的型別主要包括ReactNodeReactElementjsX.Element

  • ReactNode。表示任意型別的React節點,這是個聯合型別,包含情況眾多;
  • ReactElement/JSX。從使用表現上來看,可以認為這兩者是一致的,屬於ReactNode的子集,表示“原生的DOM元件”或“自定義元件的執行結果”。

使用示例如下:

const MyComp: React.FC<{ title: string; }> = ({title}) => <h2>{title}</h2>;

// ReactNode
const a: React.ReactNode =
  null ||
  undefined || <div>hello</div> || <MyComp title="world" /> ||
  "abc" ||
  123 ||
  true;

// ReactElement和JSX.Element
const b: React.ReactElement = <div>hello world</div> || <MyComp title="good" />;

const c: JSX.Element = <MyComp title="good" /> || <div>hello world</div>;

原生DOM相關

原生的 DOM 相關的型別,主要有以下這麼幾個:ElementHTMLElementHTMLxxxElment

簡單來說: Element = HTMLElement + SVGElement

SVGElement一般開發比較少用到,而HTMLElement卻非常常見,它的子型別包括HTMLDivElementHTMLInputElementHTMLSpanElement等等。

因此我們可以得知,其關係為:Element > HTMLElement > HTMLxxxElement,原則上是儘量寫詳細。

React合成事件相關

在 React 中,原生事件被處理成了React 事件,其內部是通過事件委託來優化記憶體,減少DOM事件繫結的。言歸正傳,React 事件的通用格式為[xxx]Event,常見的有MouseEvent

ChangeEventTouchEvent,是一個泛型型別,泛型變數為觸發該事件的 DOM 元素型別。

示例如下:

// input輸入框輸入文字
const handleInputChange = (evt: React.ChangeEvent<HTMLInputElement>) => {
  console.log(evt);
};

// button按鈕點選
const handleButtonClick = (evt: React.MouseEvent<HTMLButtonElement>) => {
  console.log(evt);
};

// 移動端觸控div
const handleDivToucwww.cppcns.com
h = (evt: React.TouchEvent<HTMLDivElement>) => { console.log(evt); };

與hooks的結合

在hooks中,並非全部鉤子都與TS有強關聯,比如useEffect就不依賴TS做型別定義,我們挑選比較常見的幾個和TS強關聯的鉤子來看看。

useState

如果初始值能說明型別,就不用給 useS程式設計客棧tate 指明泛型變數;

// ❌這樣寫是不必要的,因為初始值0已經能說明count型別
const [count,setCount] = useState<number>(0);

// ✅這樣寫好點
const [count,setCount] = useState(0);

如果初始值是 null 或 undefined,那就要通過泛型手動傳入你期望的型別,並在訪問屬性的時候通過可選鏈來規避語法錯誤。

interface IUser {
  name: string;
  age: number;
}

const [user,setUser] = React.useState<IUser | null>(null);

console.log(user?.name);

useRef

這個 hook 比較特別,它通常有兩種用途:

用來連線 DOM,以獲取到 DOM 元素;

// 連線DOM,初始值賦值為null,不能是undefined,如要指明泛型變數需要具體到HTMLxxxElement
// 如果我們確定inputRef.current在呼叫時一定是有值的,可以使用非空斷言,在null後新增!
const inputRef = useRef<HTMLInputElement>(null!);

const handleClick = () => {
  inputRef.current.focus(); // 當然不用非空斷言,用inputEl.current?.focus()可選鏈也是可以的
}

return (
  <input ref={inputRef} />
  <button onClick={handleClick}>點選</button>
)

2.用來儲存變數,由於是儲存在函式式元件的外部,比起 useState,它不會存在非同步更新的問題,也不會存在由capture-value特性引發的過時變數的問題,但是要注意賦值後由於ref引用沒變,不會引起重渲染。

// 通過初始值來自動指明泛型變數型別
const sum = useRef(0);

// 通過.current直接賦值
sum.current = 3;
// 不存在非同步更新問題
console.log(sum.current); // 3

useSelector

useSelector用於獲取store中的狀態,其第一個固定引數為函式,函式的入參即為store,而store的型別RootState需要在store中提前定義好,一種常見的定義如下:

在store.ts中:

const store = createStore(rootReducer);

export type RootState = ReturnType<typeof rootReducerBZgai>;

使用時:

const { var1,var2 } = useSelector((store: RootState) => store.xxx);

自定義 hook

如果我們需要仿照 useState 的形式,返回一個數組出去,則需要在返回值的末尾使用as const,標記這個返回值是個常量,否則返回的值將被推斷成聯合型別。

const useInfo = () => {
  const [age,setAge] = useState(0);

  return [age,setAge] as const; // 型別為一個元組,[number,React.Dispatch&程式設計客棧lt;React.SetStateAction<nBZgaiumber>>]
};

redux相關

對於action的定義,我們可以使用官方暴露的AnyAction,放寬對於action內部鍵值對的限制,如下:

import { AnyAction } from "redux";

const DEF_STATE = {
  count: 0,type: 'integer'
};

// 使用redux的AnyAction放寬限制
function countReducer(state = DEF_STATE,action: AnyAction) {
  switch (action.type) {
    case "INCREASE_COUNT":
      return {
        ...state,count: state.count + 1,};
    case "DECREASE_COUNT":
      return {
        ...state,count: state.count - 1,};
    default:
      return state;
  }
}

export default countReducer;

規約

子元件的入參命名為[元件名]Props,如:

// 比如當前元件名為InfoCard
export interface InfoCardProps {
  name: string;
  age: number;
}

interface介面型別以大寫開頭;

為後端介面的出入參書寫interface,同時使用利於編輯器提示的jsdoc風格做註釋,如:

export interface GetUserInfoReqParams {
    /** 名字 */
    name: string;
    /** 年齡 */
    age: number;
    /** 性別 */
    gender: string;
}

其他

鍵名或鍵值不確定如何處理?

// 表示鍵名不確定,鍵值限制為number型別
export interface NotSureAboutKey {
  [key: string]: number;
}

// 當鍵名鍵值都不確定時,以下介面對任何物件都是適用的
export interface AllNotSure {
  [key: string]: any;
}

如何在介面中使用泛型變數?

所謂泛型,就是預定義型別。它的目的是:達到型別定義的區域性靈活,提高複用性。我們通常會在介面中使用泛型,如:

// 通常,我們會為介面的泛型變數指定一個預設型別
interface IHuman<T = unknown> {
  name: string;
  age: number;
  gender: T;
}

// 其他地方使用時
const youngMan: IHuman<string> = {
    name: 'zhangsan',age: 18,gender: 'male'
}

到此這篇關於TypeScript在React專案中的使用總結的文章就介紹到這了,更多相關TypeScript在React使用內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!