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;
- 在React组件中推荐使用type定义Props与State,相对于interface它能更好的限制
- Typescript手册type与interface的差异
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;