亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

在React項(xiàng)目中使用TypeScript詳情

 更新時(shí)間:2022年09月20日 09:48:59   作者:???????zkat  
這篇文章主要介紹了在React項(xiàng)目中使用TypeScript詳情,文章通過圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下

前言:

本文主要記錄我如何在React項(xiàng)目中優(yōu)雅的使用TypeScript,來提高開發(fā)效率及項(xiàng)目的健壯性。

項(xiàng)目目錄及ts文件劃分

由于我在實(shí)際項(xiàng)目中大部分是使用umi來進(jìn)行開發(fā)項(xiàng)目,所以使用umi生成的目錄來做案例。

.
├── README.md
├── global.d.ts
├── mock
├── package.json
├── src
│?? ├── assets
│?? ├── components
│?? │?? └── PublicComA
│?? │??     ├── index.d.ts
│?? │??     ├── index.less
│?? │??     └── index.tsx
│?? ├── layouts
│?? ├── models
│?? ├── pages
│?? │?? ├── PageA
│?? │?? │?? ├── index.d.ts
│?? │?? │?? ├── index.less
│?? │?? │?? └── index.tsx
│?? │?? ├── index.less
│?? │?? └── index.tsx
│?? └── utils
├── tsconfig.json
├── typings.d.ts
└── yarn.lock

在項(xiàng)目根目錄下有typings.d.ts和global.d.ts這兩個(gè)文件, 前者我們可以放置一些全局的導(dǎo)出模塊,比如css,less, 圖片的導(dǎo)出聲明;后者可以放一些全局聲明的變量, 接口等, 比如說window下全局變量的聲明等。

如下:

// typings.d.ts
declare module '*.css';
declare module '*.less';
declare module "*.png";
declare module "*.jpeg";
declare module '*.svg' {
  export function ReactComponent(props: React.SVGProps<SVGSVGElement>): React.ReactElement
  const url: string
  export default url
}
// global.d.ts
interface Window {
  helloWorld: () => void;
}

接下來介紹一下src目錄:

  • assets 存放靜態(tài)資源如圖片/視頻/音頻等, 參與webpack的打包過程
  • layouts 存放公共布局
  • components 存放全局公共組件
  • models dva的models文件夾
  • pages 存放頁面的目錄, 內(nèi)部可以有頁面組件components, 結(jié)構(gòu)類似于全局的components
  • utils 存放js工具庫, 請(qǐng)求庫等公共js文件

在pages和components中有存放當(dāng)前組件/頁面所需要的類型和接口聲明的index.d.ts。另外如models中的文件由于是每個(gè)model私有類型和接口聲明,所以可以直接在文件內(nèi)部去聲明。 具體的目錄規(guī)劃如上,可以根據(jù)實(shí)際項(xiàng)目來做更合理的劃分。

在項(xiàng)目中使用TypeScript具體實(shí)踐

組件聲明

  • 函數(shù)組件 推薦使用React.FC<P={}>來表示函數(shù)類型,當(dāng)使用該類型定義組件時(shí),props中會(huì)默認(rèn)帶有children屬性。
interface IProps {
  count: number
}

const App: React.FC<IProps> = (props) => {
  const {count} = props;
  return (
    <div className="App">
      <span>count: {count}</span>
    </div>
  );
}
  • 類組件 類組件接受兩個(gè)參數(shù),第一個(gè)是props的定義,第二個(gè)是state的定義,如果使用React.PureComponent<P, S={} SS={}>定義組件,則還有第三個(gè)參數(shù),表示getSnapshotBeforeUpdate的返回值。
interface IProps {
  name: string;
}

interface IState {
  count: number;
}

class App extends React.Component<IProps, IState> {
  state = {
    count: 0
  };
  render() {
    return (
      <div>
        {this.state.count}
        {this.props.name}
      </div>
    );
  }
}

React Hooks使用

useState

聲明定義:

function useState<S>(initialState: S | (() => S)): [S, Dispatch<SetStateAction<S>>];
// convenience overload when first argument is omitted
	/**
	 * Returns a stateful value, and a function to update it.
   *
   * @version 16.8.0
   * @see https://reactjs.org/docs/hooks-reference.html#usestate
   */
    
function useState<S = undefined>(): [S | undefined, Dispatch<SetStateAction<S | undefined>>];
  /**
   * An alternative to `useState`.
   *
   * `useReducer` is usually preferable to `useState` when you have complex state logic that involves
   * multiple sub-values. It also lets you optimize performance for components that trigger deep
   * updates because you can pass `dispatch` down instead of callbacks.
   *
   * @version 16.8.0
   * @see https://reactjs.org/docs/hooks-reference.html#usereducer
   */

如果初始值能夠體現(xiàn)出類型,那么可以不用手動(dòng)聲明類型,TS會(huì)自動(dòng)推斷出類型。如果初始值為null或者undefined則需要通過泛型顯示聲明類型。

如下:

const [count, setCount] = useState(1);
const [user, setUser] = useState<IUser | null>(null);

useRef

聲明定義:

 function useRef<T>(initialValue: T): MutableRefObject<T>;
 // convenience overload for refs given as a ref prop as they typically start with a null value
 /**
   * `useRef` returns a mutable ref object whose `.current` property is initialized to the passed argument
   * (`initialValue`). The returned object will persist for the full lifetime of the component.
   *
   * Note that `useRef()` is useful for more than the `ref` attribute. It's handy for keeping any mutable
   * value around similar to how you'd use instance fields in classes.
   *
   * Usage note: if you need the result of useRef to be directly mutable, include `| null` in the type
   * of the generic argument.
   *
   * @version 16.8.0
   * @see https://reactjs.org/docs/hooks-reference.html#useref
   */

使用該Hook時(shí),要根據(jù)使用場(chǎng)景來判斷傳入泛型類型,如果是獲取DOM節(jié)點(diǎn),則傳入對(duì)應(yīng)DOM類型即可;如果需要的是一個(gè)可變對(duì)象,則需要在泛型參數(shù)中包含'| null'。

如下:

// 不可變DOM節(jié)點(diǎn),只讀
const inputRef = useRef<HTMLInputElement>(null);

// 可變,可重新復(fù)制
const idRef = useRef<string | null>(null);
idRef.current = "abc";

useCallback

聲明定義:

 function useCallback<T extends (...args: any[]) => any>(callback: T, deps: DependencyList): T;
 /**
  * `useMemo` will only recompute the memoized value when one of the `deps` has changed.
  *
  * Usage note: if calling `useMemo` with a referentially stable function, also give it as the input in
  * the second argument.
  *
  * ```ts
  * function expensive () { ... }
  *
  * function Component () {
  *   const expensiveResult = useMemo(expensive, [expensive])
  *   return ...
  * }
  * ```
  *
  * @version 16.8.0
  * @see https://reactjs.org/docs/hooks-reference.html#usememo
  */

useCallback會(huì)根據(jù)返回值自動(dòng)推斷出類型,如果傳入的參數(shù)不指定類型,則會(huì)默認(rèn)為any,所以為了嚴(yán)謹(jǐn)和可維護(hù)性,一定要指定入?yún)⒌念愋?。也可以手?dòng)傳入泛型指定函數(shù)類型。

如下:

// 會(huì)自動(dòng)推導(dǎo)出類型: (a: number, b: number) => number;
const add = useCallback((a: number, b: number) => a + b, [a, b])

// 傳入泛型,則指定函數(shù)類型
const toggle = useCallback<(a: number) => number>((a: number) => a * 2, [a])

useMemo

聲明定義:

function useMemo<T>(factory: () => T, deps: DependencyList | undefined): T;
   /**
    * `useDebugValue` can be used to display a label for custom hooks in React DevTools.
    *
    * NOTE: We don't recommend adding debug values to every custom hook.
    * It's most valuable for custom hooks that are part of shared libraries.
    *
    * @version 16.8.0
    * @see https://reactjs.org/docs/hooks-reference.html#usedebugvalue
    */

useMemo和useCallback類似,只是定義類型為具體返回值的類型,而不是函數(shù)的類型。

如下:

// 會(huì)自動(dòng)推導(dǎo)出類型: number;
const add = useCallback((a: number, b: number) => a + b, [a, b])

// 傳入泛型,則指定函數(shù)類型
const toggle = useCallback<number>((a: number) => a * 2, [a])

useContext

聲明定義:

function useContext<T>(context: Context<T>/*, (not public API) observedBits?: number|boolean */): T;
/**
  * Returns a stateful value, and a function to update it.
  *
  * @version 16.8.0
  * @see https://reactjs.org/docs/hooks-reference.html#usestate
  */

useContext會(huì)根據(jù)傳入的上下文對(duì)象自動(dòng)推導(dǎo)出context的類型,當(dāng)然也可以使用泛型來設(shè)置context的類型,

如下:

interface ITheme {
	color: string;
}
const ThemeContext = React.createContext<ITheme>({ color: "red" });

// 自動(dòng)推導(dǎo)出類型為ITheme
const theme = useContext(ThemeContext); // 等同于const theme = useContext<ITheme>(ThemeContext);

useReducer

聲明定義:

function useReducer<R extends Reducer<any, any>>(
    reducer: R,
    initialState: ReducerState<R>,
    initializer?: undefined
): [ReducerState<R>, Dispatch<ReducerAction<R>>];
/**
  * `useRef` returns a mutable ref object whose `.current` property is initialized to the passed argument
  * (`initialValue`). The returned object will persist for the full lifetime of the component.
  *
  * Note that `useRef()` is useful for more than the `ref` attribute. It's handy for keeping any mutable
  * value around similar to how you'd use instance fields in classes.
  *
  * @version 16.8.0
  * @see https://reactjs.org/docs/hooks-reference.html#useref
  */

上面只列出了一種類型定義,我在項(xiàng)目中也是使用這種定義去指定useReducer的類型。普通的案例如下:

type StateType = {
  name: string;
  age: number;
}

type Actions = {
  type: 'Change_Name';
  payload: string;
} | {
  type: 'Change_Age';
  payload: number;
}

const initialState = {
  name: '小明',
  age: 18
}

const reducerAction: Reducer<StateType, Actions> = (
  state,
  action,
) => {
  switch (action.type) {
    case 'Change_Name':
      return { ...state, name: action.payload };
    case 'Change_Age':
      return { ...state, age: action.payload };
    default:
      return state;
  }
};

function Index() {
  const [state, dispatch] = useReducer(reducerAction, initialState);
  return (
    <div>
      <div>姓名:{state.name}</div>
      <div>年齡:{state.age}</div>
    </div>
  );
}

可以看到,這樣能夠得到正確的類型推斷,但是略微繁瑣。

案例如下:

// 定義一個(gè)生成Action類型的泛型
type ActionMap<M extends Record<string, any>> = {
  [Key in keyof M]: M[Key] extends undefined
    ? {
        type: Key
      }
    : {
        type: Key
        payload: M[Key]
      }
}

type StateType = {
  name: string;
  age: number;
}

// 定義具體的Action類型
type PayloadType = {
  Change_Name: string;
  Change_Age: number;
}

/** 
  ActionMap<PayloadType>會(huì)生成類型
  {
    Change_Name: {
        type: Types.Name;
        payload: string;
    };
    Change_Age: {
        type: Types.Age;
        payload: number;
    };
  }
  而keyof ActionMap<PayloadType>則會(huì)生成 'Change_Name' | 'Change_Age'的類型。
  所以Action最終的類型便為:
  type Actions = {
      type: Types.Name;
      payload: string;
  } | {
      type: Types.Age;
      payload: number;
  }
*/
type Actions = ActionMap<PayloadType>[keyof ActionMap<PayloadType>]

const initialState = {
  name: '小明',
  age: 18
}

const reducerAction: Reducer<StateType, Actions> = (
  state,
  action,
) => {
  switch (action.type) {
    case Types.Name:
      return { ...state, name: action.payload };
    case Types.Age:
      return { ...state, age: action.payload };
    default:
      return state;
  }
};

我們定義了一個(gè)ActionMap泛型,該泛型會(huì)將傳入的類型{key: value}生成為新的{key: {type: key, payload: value }類型。然后我們利用keyof關(guān)鍵字獲取到所有的key,就可以得到我們所需要的{type: key1, payload: value1} | {type: key2, payload: value2}的類型了。只要我們定義好PayloadType類型,則可以自動(dòng)推導(dǎo)出我們需要的Actions類型。

useImperativeHandle

聲明定義:

  function useImperativeHandle<T, R extends T>(ref: Ref<T>|undefined, init: () => R, deps?: DependencyList): void;
  // NOTE: this does not accept strings, but this will have to be fixed by removing strings from type Ref<T>
  /**
   * `useImperativeHandle` customizes the instance value that is exposed to parent components when using
   * `ref`. As always, imperative code using refs should be avoided in most cases.
   *
   * `useImperativeHandle` should be used with `React.forwardRef`.
   *
   * @version 16.8.0
   * @see https://reactjs.org/docs/hooks-reference.html#useimperativehandle
   */

useImperativeHandle可以讓自定義組件通過ref屬性,將內(nèi)部屬性暴露給父組件進(jìn)行訪問。因?yàn)槭呛瘮?shù)式組件,所以需要結(jié)合forwardRef一起使用。

案例如下:

interface FancyProps {}

interface FancyRef {
  focus: () => void;
}
const FancyInput = forwardRef<FancyRef, FancyProps>((props, ref) => {
  const inputRef = useRef<HTMLInputElement>(null);
  useImperativeHandle(ref, () => ({
    focus: () => {
      inputRef.current?.focus();
    }
  }));
  return (
    <input ref={inputRef} {...props} />
  );
})
const Parent = () => {
  // 定義子組件ref
  const inputRef = useRef<FancyRef>(null);
  return (
    <div>
      <FancyInput 
        ref={inputRef}
      />
      <button 
        onClick={() => {
          // 調(diào)用子組件方法
          inputRef.current?.focus();
        }}
      >聚焦</button>
    </div>
  )
}

Axios請(qǐng)求/響應(yīng)定義封裝

axios是很流行的http庫,他的ts封裝已經(jīng)很完美了,我們只做簡單的二次封裝,返回通用的數(shù)據(jù)響應(yīng)格式。 首先在utils/request.ts中創(chuàng)建一個(gè)構(gòu)造axios實(shí)例的生成器:

import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';

// 攔截器定義
export interface RequestInterceptors {
  // 請(qǐng)求攔截
  requestInterceptors?: (config: AxiosRequestConfig) => AxiosRequestConfig
  requestInterceptorsCatch?: (err: any) => any
  // 響應(yīng)攔截
  responseInterceptors?: (config: AxiosResponse) => AxiosResponse
  responseInterceptorsCatch?: (err: any) => any
}

// 生成axios實(shí)例的參數(shù),實(shí)例可以單獨(dú)傳入攔截器
export interface RequestConfig extends AxiosRequestConfig {
  interceptorsObj?: RequestInterceptors
}

// loading請(qǐng)求數(shù)量
let loadingCount: number = 0;

// 打開loading
const showLoading = () => {
  loadingCount ++;
  if(loadingCount > 0) {
    // 顯示loading
    // Loading.show()
  }
}

// 關(guān)閉loading
const hideLoading = () => {
  loadingCount --;
  if(loadingCount <= 0) {
    // 隱藏loading
    // Loading.hide();
  }
}

function RequestBuilder(config: RequestConfig) {
  const { interceptorsObj, ...res } = config;

  const instance: AxiosInstance = axios.create(res);

  // 全局請(qǐng)求攔截器
  instance.interceptors.request.use(
    (request: AxiosRequestConfig) => {
      // 顯示loading
      showLoading();
      console.log('全局請(qǐng)求攔截器');
      // TODO:全局的請(qǐng)求頭操作等等
      return request;
    },
    (err: any) => err,
  )

  /**
   * 實(shí)例請(qǐng)求攔截器 
   * 要注意 axios請(qǐng)求攔截器為倒序執(zhí)行,所以要將實(shí)例請(qǐng)求攔截器注冊(cè)在全局請(qǐng)求攔截器后面
   */
  instance.interceptors.request.use(
    interceptorsObj?.requestInterceptors,
    interceptorsObj?.requestInterceptorsCatch,
  )

  /**
   * 實(shí)例響應(yīng)攔截器
   * axios響應(yīng)攔截器為正序執(zhí)行,所以要將實(shí)例響應(yīng)攔截器注冊(cè)在全局響應(yīng)攔截器前面
   */
  instance.interceptors.response.use(
    interceptorsObj?.responseInterceptors,
    interceptorsObj?.responseInterceptorsCatch,
  )
  
  // 全局響應(yīng)攔截器
  instance.interceptors.response.use(
    (response: AxiosResponse) => {
      console.log('全局響應(yīng)攔截器');
      // 關(guān)閉loading
      hideLoading();
      // TODO: 通用的全局響應(yīng)處理,token過期重定向登錄等等
      // 返回值為res.data,即后端接口返回的數(shù)據(jù),減少解構(gòu)的層級(jí),以及統(tǒng)一響應(yīng)數(shù)據(jù)格式。
      return response.data
    },
    (err: any) => {
      // 關(guān)閉loading
      hideLoading();
      // TODO: 錯(cuò)誤提示等
      return err;
    },
  )

  return instance;
}
export const http = RequestBuilder({baseURL: '/api'});

該生成器可以實(shí)現(xiàn)每個(gè)實(shí)例有單獨(dú)的攔截器處理邏輯,并且實(shí)現(xiàn)全局的loading加載效果,全局?jǐn)r截器的具體實(shí)現(xiàn)可以根據(jù)項(xiàng)目實(shí)際需求進(jìn)行填充。生成器已經(jīng)完成,但是還沒法定制我們的通用響應(yīng)數(shù)據(jù),接下來我們?cè)?code>typings.d.ts中重新定義axios模塊:

import * as axios from 'axios';
declare module 'axios' {
  // 定制業(yè)務(wù)相關(guān)的網(wǎng)絡(luò)請(qǐng)求響應(yīng)格式, T 是具體的接口返回類型數(shù)據(jù)
  export interface CustomSuccessData<T> {
    code: number;
    msg?: string;
    message?: string;
    data: T;
    [keys: string]: any;
  }
  export interface AxiosInstance {
    // <T = any>(config: AxiosRequestConfig): Promise<CustomSuccessData<T>>;
    request<T = any, R = CustomSuccessData<T>, D = any>(config: AxiosRequestConfig<D>): Promise<R>;
    get<T = any, R = CustomSuccessData<T>, D = any>(url: string, config?: AxiosRequestConfig<D>): Promise<R>;
    delete<T = any, R = CustomSuccessData<T>, D = any>(url: string, config?: AxiosRequestConfig<D>): Promise<R>;
    head<T = any, R = CustomSuccessData<T>, D = any>(url: string, config?: AxiosRequestConfig<D>): Promise<R>;
    post<T = any, R = CustomSuccessData<T>, D = any>(
      url: string,
      data?: D,
      config?: AxiosRequestConfig<D>,
    ): Promise<R>;
    put<T = any, R = CustomSuccessData<T>, D = any>(
      url: string,
      data?: D,
      config?: AxiosRequestConfig<D>,
    ): Promise<R>;
    patch<T = any, R = CustomSuccessData<T>, D = any>(
      url: string,
      data?: D,
      config?: AxiosRequestConfig<D>,
    ): Promise<R>;
  }
}

完成以上操作后,我們?cè)跇I(yè)務(wù)代碼中具體使用:

import { http } from '@/utils/request';

interface Req {
  userId: string;
}

interface Res {
  userName: string;
  userId: string;
}

// 獲取用戶信息接口
const getUserInfo = async (params: Req) => {
  return http.get<Res>('/getUserInfo', {params})
}

這個(gè)時(shí)候getUserInfo返回的就是CustomSuccessData<Res>類型的數(shù)據(jù)了。至此我們對(duì)axios簡單的封裝也就完成了。

到此這篇關(guān)于在React項(xiàng)目中使用TypeScript詳情的文章就介紹到這了,更多相關(guān)React使用TypeScript內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • React實(shí)現(xiàn)復(fù)雜搜索表單的展開收起功能

    React實(shí)現(xiàn)復(fù)雜搜索表單的展開收起功能

    本節(jié)對(duì)于需要展開收起效果的查詢表單進(jìn)行概述,主要涉及前端樣式知識(shí)。對(duì)React實(shí)現(xiàn)復(fù)雜搜索表單的展開-收起功能感興趣的朋友一起看看吧
    2021-09-09
  • React添加或移除類的操作方法

    React添加或移除類的操作方法

    這篇文章主要介紹了React添加或移除類的操作方法,本文通過示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-05-05
  • React受控組件與非受控組件深入講解

    React受控組件與非受控組件深入講解

    具體來說這是一種react非受控組件,其狀態(tài)是在input的react內(nèi)部控制,不受調(diào)用者控制??梢允褂檬芸亟M件來實(shí)現(xiàn)。下面就說說這個(gè)React中的受控組件與非受控組件的相關(guān)知識(shí),感興趣的朋友一起看看吧
    2022-12-12
  • React函數(shù)式組件Hook中的useState函數(shù)的詳細(xì)解析

    React函數(shù)式組件Hook中的useState函數(shù)的詳細(xì)解析

    Hook 就是 JavaScript 函數(shù),這個(gè)函數(shù)可以幫助你鉤入(hook into) React State以及生命周期等特性,這篇文章主要介紹了React Hook useState函數(shù)的詳細(xì)解析的相關(guān)資料,需要的朋友可以參考下
    2022-10-10
  • React里的Fragment標(biāo)簽的具體使用

    React里的Fragment標(biāo)簽的具體使用

    本文主要介紹了React里的Fragment標(biāo)簽的具體使用,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-01-01
  • React封裝彈出框組件的方法

    React封裝彈出框組件的方法

    這篇文章主要為大家詳細(xì)介紹了React封裝彈出框組件的方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-08-08
  • react-router-domV6版本的路由和嵌套路由寫法詳解

    react-router-domV6版本的路由和嵌套路由寫法詳解

    本文主要介紹了react-router-domV6版本的路由和嵌套路由寫法詳解,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • 詳解Jotai Immer如何實(shí)現(xiàn)undo redo功能示例詳解

    詳解Jotai Immer如何實(shí)現(xiàn)undo redo功能示例詳解

    這篇文章主要為大家介紹了詳解Jotai Immer如何實(shí)現(xiàn)undo redo功能示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-04-04
  • ReactNative集成個(gè)推消息推送過程詳解

    ReactNative集成個(gè)推消息推送過程詳解

    這篇文章主要為大家介紹了ReactNative集成個(gè)推消息推送過程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-08-08
  • 實(shí)例講解React 組件

    實(shí)例講解React 組件

    這篇文章主要介紹了React 組件的相關(guān)資料,文中示例代碼非常詳細(xì),幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下
    2020-07-07

最新評(píng)論