React遇见Typescript

774 阅读2分钟

dirk-ribbler-xEFoRSMT-x4-unsplash.jpg

React与Typescript结合使用,日常总是看vscode提示类型来写,来记录一下,后续继续补充。

定义组件props类型

import React from 'react';

// type
type AppProps = {
  name: string;
  children: React.ReactNode;
  status: 'success' | 'error';
};

// interface
interface AppProps {
  name: string;
  children: React.ReactNode;
  status: 'success' | 'error';
}

const App = ({ name, children, status }: AppProps) => (
  <div>
    <span>{name}</span>
    <span>{status}</span>
    {children}
  </div>
);

export default App;

JSX.Element与React.ReactNode ?

type AppProps = {
  children: JSX.Element | React.ReactNode;
};

const App = ({ children }: AppProps) => {
  return <>children</>;
};

const Index = () => {
  return (
    // 当children为JSX.Element,children只能是jsx元素标签,不可以是字符串,例如:<App>文字</App>
     <App>
       <span>asda</span>
     </App>
    // 当children为React.ReactNode,children可以是任意元素
    <App>一段描述</App>
  );
};

export default Index;

// React.ReactNode是组件所有可能的返回类型的集合

定义Function Components

import React from 'react';

type AppProps = {
  name: string;
};

// React.FC定义props类型,会有默认的children属性
const App: React.FC<AppProps> = ({ name, children }) => <div>{name}</div>;

// 不需要children用参数类型方式即可 或者 React.VoidFunctionComponent<AppProps>
const App = ({ name }: AppProps) => <div>{name}</div>;

export default App;

useState与useRef

const App = () => {
  // 默认为undefined
  const [name, setName] = useState<string | undefined>();
  // ref
  const divRef = useRef<HTMLDivElement>(null);

  return <div ref={divRef}>{name}</div>;
};

useImperativeHandle

// 父组件调用子组件的方法,forwardRef第一个类型是ref上挂载的属性,第二个类型是props类型
type Props = { name: string };
type Ref = {
  sayHello: () => void;
};

const Child = forwardRef<Ref, Props>(({ name }, ref) => {
  useImperativeHandle(ref, () => ({
    sayHello: () => console.log('hello'),
  }));

  return <div>{name}</div>;
});

const Parent = () => {
  const childRef = useRef<Ref>(null);

  return (
    <div>
      <Child ref={childRef} name="child"></Child>
    </div>
  );
};

useContext

import React from 'react';

interface AppContextInterface {
  name: string;
}

// null!表示值非null,一定存在
const AppCtx = React.createContext<AppContextInterface>(null!);

const sampleAppContext: AppContextInterface = {
  name: 'Typescript React App',
};

export const App = () => (
  <AppCtx.Provider value={sampleAppContext}>...</AppCtx.Provider>
);

export const PostInfo = () => {
  const appContext = React.useContext(AppCtx);
  return <div>Name: {appContext.name}</div>;
};

createPortal

import React, { useEffect, useRef } from 'react';
import { createPortal } from 'react-dom';

const modalRoot = document.body;

const Modal: React.FC<{}> = ({ children }) => {
  const el = useRef(document.createElement('div'));

  useEffect(() => {
    const current = el.current;
    modalRoot.appendChild(current);
    return () => void modalRoot.removeChild(current);
  }, []);

  return createPortal(children, el.current);
};

export default Modal;
// createPortal可以将组件渲染到指定元素中,如antd的Modal

ts一些使用技巧

// !表示类型推断排除undefined和null
document.getElementById('root')!.parentNode;

// 类型联合使用
type AnimalType = {
  name: string;
};

type DogType = {
  age: number;
};

const Dog = ({ name, age }: AnimalType & DogType) => (
  <div>
    {name}
    {age}
  </div>
);

// 通过interface继承实现
interface AnimalInterface {
  name: string;
}

interface DogInterface extends AnimalInterface {
  age: string;
}

const Dog = ({ name, age }: DogInterface) => (
  <div>
    {name}
    {age}
  </div>
);
// 定义函数类型
type FunctionType = (x: number, y: number) => number;
type FunctionType1 = (x: number, y: number) => Promise<number>;
// 函数重载
// 重载:方法名相同,参数类型不同,返回值类型也可以不同
function renderTable(data: { name: string; age: number }): number;
function renderTable(data: string): string;
function renderTable(data): any {
  // bala bala bala....
}
renderTable({ name: '1', age: 1 });
renderTable('hello');
// typeof 获取变量的类型,不必重新声明
const App = () => {
  const [state, setState] = React.useState({
    name: '小明',
    age: 2,
  }); 

  const method = (obj: Partial<typeof state>) => {
    setState(obj); 
  };
}
// Partial<typeof state> -> { name?:string; age: number; }
// Omit从一个类型中剔除某属性
type User = {
  id: string;
  name: string;
  email: string;
};

type UserWithoutEmail = Omit<User, 'email'>;

Omit<User, 'email'> 相当于 type UserWithoutEmail = {
  id: string;
  name: string;
};

模仿写一个useSafeState hooks

// 中心思想就是用一个变量标记组件是否卸载,如果卸载就不setState,这样就不会造成内存泄露等意外情况
import { useState, useEffect, useRef, Dispatch, SetStateAction } from 'react';

function useSafeState<S>(initialState: S | (() => S)): [S, Dispatch<SetStateAction<S>>];

function useSafeState<S = undefined>(): [S | undefined, Dispatch<SetStateAction<S | undefined>>,];

function useSafeState<T>(initialValue?: T) {
  const [state, setState] = useState(initialValue);
  const isUnMounted = useRef(false);

  useEffect(() => {
    return () => {
      isUnMounted.current = true;
    };
  }, []);

  const setSafeState = (value: T) => {
    if (!isUnMounted.current) {
      setState(value);
    }
  };
  return [state, setSafeState] as const;
}

export default useSafeState;