React中常見的TypeScript定義使用

前言

在我學習typescript時,想在react中使用typescript寫代碼,從頭開始的時候是懵逼的,由於官方文檔並無使用typescript的教程,可能是本身在網上查,本身看定義摸索html

因此今天把我用過的,總結概括一下,但願能幫助到一樣在摸索的同窗react

如下代碼react版本爲16.13.1,在create-react-app官方typescript模版中無報錯typescript

類型簡述

一些React的內置類型express

  • React.ReactElement —— 使用React.createElement建立的,能夠簡單理解爲React中的JSX的元素
  • React.ReactNode —— <div>xxx</div> xxx的合法類型
  • React.CSSProperties —— 組件內聯的style對象的類型
  • React.RefObject —— React.createRef建立的類型,只讀不可改
  • React.MutableRefObject —— useRef建立的類型,能夠修改
  • ...

組件聲明

組件聲明分爲類組件和函數組件數組

類組件

類組件使用的定義主要爲React.Component<P,S>React.PureComponent<P,S,SS>app

interface AppProps {
  value: string;
}
interface AppState {
  count: number;
}
class App extends React.Component<AppProps, AppState> {
  static defaultProps = {
    value: "",
  };
  state = {
    count: 0,
  };
}
複製代碼

React.Component<P,S>這裏的Pprops的類型,Sstate的類型,能夠寫爲React.Component<AppProp>,由於state的類型會本身推斷函數

在上面PureComponent中還有個SS,這個SSgetSnapshotBeforeUpdate的返回值工具

函數組件

函數組件定義的方式簡單來看有兩種,一種是使用React.FC,一種是直接給props寫上定義學習

interface AppProps {
  value?: string;
}

const App: React.FC<AppProps> = ({ value = '', children }) => {
    // ...
};
複製代碼

React.FC的意思是FunctionComponent,若是使用了React.FC,它在定義裏就已經規定好了children的類型和函數的返回值,因此能夠直接用childrenui

若是是直接給props寫上定義,就須要本身定義children的類型

interface AppProps {
  value?: string;
  children?: React.ReactNode; // 本身定義children的類型
}

function App({ value = "", children }: AppProps) {
  return <>{children}</>;
}
複製代碼

使用function來定義而不是箭頭函數的優勢是可使用泛型組件

Hooks聲明

hooks的聲明若是不知道如何用ts定義能夠直接點進去看看

useState

useState可使用泛型傳參或者自動推斷

const [state, setState] = useState(''); // state的類型爲string,自動推斷
const [state, setState] = useState<string>(); // state的類型爲 string | undefined
// 給初值
const [state, setState] = useState<string | null>(null); // state的類型爲 string | null
複製代碼

useRef

useRef一樣也會自動推斷

const ref = useRef(""); // ref.current的類型爲 string
// 泛型
type Value = { value: string };
const ref = useRef<Value>({ value: "" });
// ref爲html元素
const ref = useRef<HTMLDivElement>(null);
return <div ref={ref} />;
複製代碼

須要注意的是若是ref爲元素,那麼初始值得寫個null纔不會報錯

useReducer

useReducer相對來講要寫的更多一點,能夠自動推斷,因此不須要手動寫泛型類型(其實我也不知道手動寫怎麼寫Orz)

// state類型
interface ReducerState {
  value: string;
}
// action類型
interface AnyAction {
  type: string;
  [key: string]: any;
}
// reducer函數
const reducer: React.Reducer<ReducerState, AnyAction> = (state, action) => {
  switch (action.type) {
    default:
      return state;
  }
};
// 初始值
const initialState: ReducerState = { value: "" };

const [state, dispatch] = useReducer(reducer, initialState);
// state 的類型爲 ReducerState
// dispatch 的類型爲 React.Dispatch<AnyAction>
複製代碼

Action也能夠是多個不一樣的Action的聯合類型

useImperativeHandle

useImperativeHandle這個鉤子能夠把內部方法經過ref暴露出去,用ts也是要寫多一點,類型都須要標註清楚

因此須要使用到React.forwardRef,能夠先看下一節

// props
interface AppProps {
  value: string;
}
// useImperativeHandle獲取到ref的類型
interface Handle {
  get: () => string;
}

const App = React.forwardRef<Handle, AppProps>(({ value }, ref) => {
  // 定義
  useImperativeHandle(ref, () => ({
    get: () => `handle get value : ${value}`,
  }));
  return null;
});
// 使用
const handleRef = useRef<Handle>(null);
// handleRef.current?.get();
return <App value="hello" ref={handleRef} />;
複製代碼

自定義hook須要注意的

有以下鉤子

const useCustomHook = () => {
  const [state, setState] = useState("");
  const set = (value: string) => {
    if (!value) return;
    setState(value);
  };
  return [state, set];
};
// 使用
const [state, setState] = useCustomHook();
setState('hello') // This expression is not callabl
複製代碼

自定鉤子還須要定義返回值才行

- const useCustomHook = () => {
+ const useCustomHook = (): [string, (value: string) => void] => {
複製代碼

React.forwardRef

React.forwardRef<T, P = {}>只須要傳props的類型和ref的類型,第一個Tref的類型,Pprops的類型

const App = React.forwardRef<HTMLDivElement, AppProps>(({ value }, ref) => {
  return <div ref={ref} />;
});
// 使用
const ref = useRef<HTMLDivElement>(null);
return <App value="hello" ref={ref} />;
複製代碼

React.ForwardRefRenderFunction

定義爲該類型的函數能夠放進React.forwardRef函數中做爲參數

// 定義
const forwardRender: React.ForwardRefRenderFunction<
  HTMLDivElement,
  AppProps
> = ({ value }, ref) => {
  return <div ref={ref} />;
};
const App = React.forwardRef(forwardRender);
// 使用
const ref = useRef<HTMLDivElement>(null);
return <App value="hello" ref={ref} />;
複製代碼

React.createContext

泛型有自動推斷的功能,因此useContext就不須要再寫上類型了

interface ContextType {
  getPrefixCls: (value: string) => string;
}

const context = React.createContext<ContextType>({
  getPrefixCls: (value) => `prefix-${value}`,
});

const App = () => {
  const { getPrefixCls } = useContext(context);
  getPrefixCls("App"); // prefix-App
  return null;
};
複製代碼

React.cloneElement

若是使用的React.FC定義的組件,它的children類型默認是React.ReactNode,須要顯式轉爲React.ReactElement

const App: React.FC = ({ children }) => {
  return React.cloneElement(children as React.ReactElement, { value: "hello" });
};
// 也能夠覆寫定義
const App: React.FC<{ children: React.ReactElement }> = ({ children }) => {
  return React.cloneElement(children, { value: "hello" });
};
複製代碼

React.ComponentType

經過React.ComponentType<P>定義的組件能夠將變量名傳入組件,在組件內調用,高階組件一般會使用

interface AppProps {
  value: string;
}
const App: React.FC<AppProps> = (props) => {
  return null;
};
// React.ComponentType定義組件
function HOC<T>(Component: React.ComponentType<T>) {
  return function (props: T) {
    return <Component {...props} />;
  };
}
const WrappedComponent = HOC(App);
// 調用
<WrappedComponent value="hello" />
複製代碼

泛型參數的組件

泛型參數的組件是typescript2.9版本新增的,第一次看見是在ant-deisgn

一個很簡單的例子就是Select組件

<Select<number>>
  <Select.Option value={1}>1</Select.Option>
  <Select.Option value={2}>2</Select.Option>
</Select>
複製代碼

類組件的定義

// 定義泛型參數的組件
class GenericComponent<P> extends React.Component<P> {
  internalProp: P;
  constructor(props: P) {
    super(props);
    this.internalProp = props;
  }
  render() {
    return null;
  }
}

type Props = { a: number; b: string };

<GenericComponent<Props> a={10} b="hi" />; // OK
<GenericComponent<Props> a={10} b={20} />; // Error
複製代碼

函數組件的定義

function GenericComponent<P>(props: P) {
  const internalProp = useRef(props)
  return null;
}
複製代碼

箭頭函數的組件在特定條件也能用泛型

// 這樣會解析錯誤
const GenericComponent = <P>(props: P) =>{
  const internalProp = useRef(props);
  return null;
}

// 泛型必須使用extends關鍵字才能解析
const GenericComponent = <P extends any>(props: P) =>{
  const internalProp = useRef(props);
  return null;
}
複製代碼

函數組件寫起來可簡潔太多了…

事件處理

也是多種多樣

const App = () => {
  // React.MouseEventHandler
  const onClick: React.MouseEventHandler<HTMLInputElement> = (e) => {
    console.log(e.currentTarget.value);
  };
  // React.ChangeEventHandler
  const onChange: React.ChangeEventHandler<HTMLInputElement> = (e) => {
    console.log(e.currentTarget.value);
  };
  // React.FocusEventHandler
  const onFocus: React.FocusEventHandler<HTMLInputElement> = (e) => {
    console.log(e.currentTarget.value);
  };
  return <input onClick={onClick} onChange={onChange} onFocus={onFocus} />;
};
複製代碼

若是有事件不清楚該如何定義類型,能夠點組件上的事件名去看看定義

須要注意的是隻有e.currentTarget纔會有對應的元素類型,e.target是沒有的,它的類型是EventTarget

總結

想不出還有啥了,想起來再補充

ts自帶了一些工具泛型,好比OmitPick,在開發的時候仍是有幫助,能夠看看我之前的總結

最後,祝你們身體健康,工做順利!


太慘了,tsx連代碼高亮都沒有,只能說仍是prismjs好使,highlightjs不太行

相關文章
相關標籤/搜索