使用typeScript开发React的最佳实战可以参考这里:https://react-typescript-cheatsheet.netlify.app/docs/basic/setup
开发
组件的Props
基础类型、对象类型、函数类型
type AppProps = {message: string;count: number;disabled: boolean;/** array of a type! */names: string[];/** string literals to specify exact string values, with a union type to join them together */status: "waiting" | "success";/** any object as long as you dont use its properties (NOT COMMON but useful as placeholder) */obj: object;obj2: {}; // almost the same as `object`, exactly the same as `Object`/** an object with any number of properties (PREFERRED) */obj3: {id: string;title: string;};/** array of objects! (common) */objArr: {id: string;title: string;}[];/** a dict object with any number of properties of the same type */dict1: {[key: string]: MyTypeHere;};dict2: Record<string, MyTypeHere>; // equivalent to dict1/** any function as long as you don't invoke it (not recommended) */onSomething: Function;/** function that doesn't take or return anything (VERY COMMON) */onClick: () => void;/** function with named prop (VERY COMMON) */onChange: (id: number) => void;/** alternative function type syntax that takes an event (VERY COMMON) */onClick(event: React.MouseEvent<HTMLButtonElement>): void;/** an optional prop (VERY COMMON!) */optional?: OptionalType;};
React相关的类型
export declare interface AppProps {children1: JSX.Element; // bad, doesnt account for arrayschildren2: JSX.Element | JSX.Element[]; // meh, doesn't accept stringschildren3: React.ReactChildren; // despite the name, not at all an appropriate type; it is a utilitychildren4: React.ReactChild[]; // better, accepts array childrenchildren: React.ReactNode; // best, accepts everything (see edge case below)functionChildren: (name: string) => React.ReactNode; // recommended function as a child render prop typestyle?: React.CSSProperties; // to pass through style propsonChange?: React.FormEventHandler<HTMLInputElement>; // form events! the generic parameter is the type of event.target// more info: https://react-typescript-cheatsheet.netlify.app/docs/advanced/patterns_by_usecase/#wrappingmirroringprops: Props & React.ComponentPropsWithoutRef<"button">; // to impersonate all the props of a button element and explicitly not forwarding its refprops2: Props & React.ComponentPropsWithRef<MyButtonWithForwardRef>; // to impersonate all the props of MyButtonForwardedRef and explicitly forwarding its ref}
函数式组件
最简单的
interface AppProps = { message: string };const App = ({ message }: AppProps) => <div>{message}</div>;
包含children的
利用内置的React.FC 内置的类型的话,除了包含定义的Props还会自动的增加一个children类型。
// 等同于AppProps & {children: React.ReactNodepropTypes?: WeakValidationMap<P>;contextTypes?: ValidationMap<any>;defaultProps?: Partial<P>;displayName?: string;}// 使用interface AppProps = { message: string };const App: React.FC<AppProps> = ({ message, children }) => {return (<>{children}<div>{message}</div></>)};
Hooks
useState
默认值可以说明类型,不用手动说明交给ts自动推断即可
// val: booleanconst [val, toggle] = React.useState(false);toggle(false)toggle(true)
初始值是null或者是undefined
const [user, setUser] = React.useState<IUser | null>(null);// later...setUser(newUser);
useReducer
用联合类型(Discriminated Unions)来标注 Action 的类型
const initialState = { count: 0 };type ACTIONTYPE =| { type: "increment"; payload: number }| { type: "decrement"; payload: string };function reducer(state: typeof initialState, action: ACTIONTYPE) {switch (action.type) {case "increment":return { count: state.count + action.payload };case "decrement":return { count: state.count - Number(action.payload) };default:throw new Error();}}function Counter() {const [state, dispatch] = React.useReducer(reducer, initialState);return (<>Count: {state.count}<button onClick={() => dispatch({ type: "decrement", payload: "5" })}>-</button><button onClick={() => dispatch({ type: "increment", payload: 5 })}>+</button></>);}
传入特定的type时,剩下的类型payload就会自动匹配推断。
useEffect
useEffect的返回值需要是一个方法或者是undefined,可以用自执行函数
useEffect(() => {(async () => {const user = await getUser()setUser(user)})()}, [])
useRef
useImperativeHandle
自定义hook
export function useLoading() {const [isLoading, setState] = React.useState(false);const load = (aPromise: Promise<any>) => {setState(true);return aPromise.finally(() => setState(false));};// ✅ 加了 as const 会推断出 [boolean, typeof load]// ❌ 否则会是 (boolean | typeof load)[]return [isLoading, load] as const;[]}
React API
forwardRef
子组件
type Props = { };export type Ref = HTMLButtonElement;export const FancyButton = React.forwardRef<Ref, Props>((props, ref) => (<button ref={ref} className="MyClassName">{props.children}</button>));
父组件使用,将ref转发给button
export const App = () => {const ref = useRef<HTMLButtonElement>()return (<FancyButton ref={ref} />)}
事件
input的onChange
const ColorSelectInput: React.FC<{}> = () => {const [color, setColor] = React.useState<string>("blue")//定义事件changeColorconst changColor = (e: React.ChangeEvent<HTMLInputElement>) => {setColor(e.target.value)//input中onChange调用changeColor}return <input value={color} onChange={changColor}/>}
