You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
typeProps={children: React.ReactNode;};functionComp({ children }: Props){return<div>{children}</div>;}functionApp(){return<Comp>{{}}</Comp>;// Runtime Error: Objects not valid as React Child!}
constGreetComponent=({ name, age, status }: RequiredProps&DefaultProps)=>(<div>{`Hello, my name is ${name}, ${age}, ${typeofstatus===string ? status : status[0]}`}</div>);constdefaultProps={age: 25,status: ""}asDefaultProps;GreetComponent.defaultProps=defaultProps;typeRequiredProps={name: string;}typeDefaultProps={age: number,status: string|string[]}
interfaceIProps{name: string;}constdefaultProps={age: 25,};constGreetComponent=({ name, age }: IProps&typeofdefaultProps)=>(<div>{`Hello, my name is ${name}, ${age}`}</div>);GreetComponent.defaultProps=defaultProps;// React.ComponentProps<T>:获取组件 T 的 props 类型constTestComponent=(props: React.ComponentProps<typeofGreetComponent>)=>{return<h1/>;};// Error: Property 'age' is missing in type '{ name: string; }' but required in type '{ age: number; }'constel=<TestComponentname="foo"/>;
functioncreateCtx<Aextends{}|null>(){constctx=React.createContext<A|undefined>(undefined);functionuseCtx(){constc=React.useContext(ctx);if(c===undefined)thrownewError("useCtx must be inside a Provider with a value");returnc;}return[useCtx,ctx.Provider]asconst;// 'as const' 使 TypeScript 推断出一个元组}// 使用:exportconst[useCurrentUserName,CurrentUserProvider]=createCtx<string>();functionEnthusasticGreeting(){constcurrentUser=useCurrentUserName();return<div>HELLO {currentUser.toUpperCase()}!</div>;}functionApp(){return(<CurrentUserProvidervalue="Anders"><EnthusasticGreeting/></CurrentUserProvider>);}
本文大部分内容翻译至 React TypeScript Cheatsheet,并根据个人感受补充、阉割了部分内容,想更详细了解可查看原文。《React 和 Typescript(下)》准备整理常见问题及解决方案。
开始
安装依赖
devDependencies
:typescript
@types/react
: React 类型定义@types/react-dom
:React DOM 类型定义dependencies
:react
react-dom
tslib
:TypeScript 辅助函数库。TypeScript 通过一些辅助函数实现降级(转换为旧版本的 JavaScript),这些辅助函数可能导致大量重复代码,在tsconfig.json
开启importHelpers
选项后辅助函数将从 tslib 导入。tsconfig.json
更多配置项可查看tsconfig.json 详解。
Prop Types
React.ReactNode
极端情况这是因为
ReactNode
包含ReactFragment
(它允许{}
),修复这个会破坏很多库,所以你只需要注意ReactNode
并不是绝对安全。React.ReactNode
vsJSX.Element
有效的 React node 与
React.createElement
返回的内容不同。无论组件最终渲染什么,React.createElement
始终返回一个对象,即JSX.Element
接口,但React.ReactNode
是组件所有可能返回值的集合。即:JSX.Element
->React.createElement
的返回值React.ReactNode
-> 组件的返回值type
orinterface
?在创建库或第三方的类型定义时,始终使用
interface
作为公共 API 的定义,因为这允许使用者在缺少某些定义时通过声明合并来扩展。而你的 React 组件
Props
和State
考虑使用Type
以保持一致性,因为它受到更多限制。type
对于联合类型(例如type MyType = TypeA | TypeB
)很有用,而interface
更适合声明数据模型然后可以实现(implements
)或扩展(extends
)。Components
Function Components
一个普通函数,函数接受一个
props
参数并返回一个JSX.Element
。不推荐使用
React.FC
?React.FC
即React.FunctionComponent
的简写,它没有明显好处却存在几个主要缺点:props.children
,意味着所有组件都可接受children
,但实际可能并不需要;component as namespace pattern
(使用组件作为相关组件(常为子组件)的命名空间)中(如<Select.Item />
),使用React.FC
没有意义;defaultProps
无法正常工作。具体可查看Remove React.FC from Typescript template。
Class Components
State
类型声明不是必须的;Props
和State
中的readonly
修饰符是不多余的,因为React.Component<P,S>
已经将它们标记为不可变的。Class Properties
(类属性):如果需要声明类属性供以后使用,只需像state
一样声明它,但不要赋值。Typing getDerivedStateFromProps
通过
getDerivedStateFromProps
可以从props
获取派生状态,下面是一些定义getDerivedStateFromProps
的方法:getDerivedStateFromProps
返回值符合它getDerivedStateFromProps
的返回值确定State
类型DefaultProps
你可能不需要
defaultProps
。因为根据这条推文defaultProps
最终将被弃用,共识是使用对象默认值。Function Components
Class components
defaultProps
类型defaultProps
类型推断在 TypeScript 3.0+ 中得到了很大改进,尽管一些极端情况仍然存在问题。Function Components
明确定义
DefaultProps
类型在某些情况下,我们可能希望
defaultProps
有多种类型。这种方式可以灵活地分配两种类型。此外它还会检查defaultProp
的类型验证,例如👇Class components
JSX.LibraryManagedAttributes
上面的实现对于应用程序来说是很好的方式,但是有时你希望能够
export GreetProps
以便其他人可以使用。这里的问题在于GreetProps
的定义方式,age
是一个必需的属性,而不是defaultProps
。你可以为了
export
专门创建单独的类型,也可以使用JSX.LibraryManagedAttributes
(提取必需和可选的 Props):使用
defaultProps
消费组件的 Props问题:
解决方案:定义一个应用了
LibraryManagedAttributes
的工具👇Events
React.SyntheticEvent
;onClick(e: React.MouseEvent) { ... }
。事件类型列表
AnimationEvent
: CSS 动画事件。ChangeEvent
: 当用户更改<input>
,<select>
和<textarea>
元素的值并提交这个更改时触发的事件。ClipboardEvent
: 使用复制、粘贴和剪切事件。CompositionEvent
: 由于用户间接输入文本而发生的事件(例如用户使用拼音输入法开始输入汉字时,这个事件就会被触发)。DragEvent
: 与指针设备(例如鼠标)的拖放交互。FocusEvent
: 当元素获得或失去焦点时发生的事件。FormEvent
: 每当表单或表单元素获得/失去焦点、表单元素值更改或表单提交时发生的事件。InvalidEvent
: 当输入的有效性限制失败时触发的事件(例如<input type="number" max="10">
,但有人输入数字 20)。KeyboardEvent
: 键盘事件。MouseEvent
: 用户与指针设备(如鼠标)交互时发生的事件。PointerEvent
: 用于在定点输入设备(鼠标、触控笔和单点或多点的手指触摸)上交互所触发的事件。TouchEvent
: 用户与触摸设备交互而发生的事件。TransitionEvent
: CSS 过渡事件。WheelEvent
: 用户滚动鼠标滚轮或类似输入设备时触发的事件。SyntheticEvent
: 以上所有事件的基础事件,当不确定事件类型时应该使用。关于
InputEvent
Typescript 不支持
InputEvent
,因为这是一个实验中的功能,并不是所有浏览器都完全支持,并且在不同的浏览器中可能表现不同。你可以改用KeyboardEvent
。Hooks
useState
useReducer
useEffect
确保返回函数或
undefined
。useLayoutEffect
会在所有的 DOM 变更之后同步调用 effect,使用方式与useEffect
基本一致。useRef
访问 DOM
保存可变值
useImperativeHandle
useMemo & useCallback
useCallback
Context
创建一个没有
defaultValue
且不需要检查undefined
的createCtx
上面的写法略显多余,因为随后就在
Provider
设置了有效的content
。有几个解决方案:简单但不太安全,你有可能忘记向
Provider
提供值。createCtx
的辅助函数,以防止访问未提供值的Context
。这样不需要提供defaultValue
,也不需要检查undefined
forwardRef/createRef
createRef
从
forwardRef
得到的ref
是可变的,所以你可以根据需要指定它。如果你希望使其不可变(即确保没人可以重新指定它),请指定React.Ref
:如果你想要获取
forward
中ref
组件的 props,请使用 ComponentPropsWithRef。参考
The text was updated successfully, but these errors were encountered: