JSX 的类型
在 App.tsx 里开始练习 TypeScript 类型:
interface AaaProps {
name: string;
}
function Aaa(props: AaaProps) {
return <div>aaa, {props.name}</div>
}
function App() {
return <div>
<Aaa name="guang"></Aaa>
</div>
}
export default App;
其实组件我们一般不写返回值类型,就用默认推导出来的。
React 函数组件默认返回值就是 JSX.Element。
我们看下 JSX.Element 的类型定义:
const content: JSX.Element = <div>aaa</div>
可以看到它就是 React.ReactElement。
也就是说,如果你想描述一个 jsx 类型,就用 React.ReactElement 就好了。
比如 Aaa 组件有一个 content 的 props,类型为 ReactElement:
这样就只能传入 JSX。
ReactElement 就是 jsx 类型,但如果你传入 null、number 等就报错了:
那如果有的时候就是 number、null 呢?
换成 ReactNode 就好了:
看下它的类型定义:
ReactNode 包含 ReactElement、或者 number、string、null、boolean 等可以写在 JSX 里的类型。
这三个类型的关系 ReactNode > ReactElement > JSX.Element。
所以,一般情况下,如果你想描述一个参数接收 JSX 类型,就用 ReactNode 就行。
函数组件的类型
前面的函数组件,我们都没明确定义类型:
其实它的类型是 FunctionComponent:
看下它的类型定义:
可以看到,FC 和 FunctionComponent 等价,参数是 Props,返回值是 ReactNode。
hook 的类型
useState
一般用推导出的类型就行:
也可以手动声明类型:
useRef
useRef 我们知道,可以保存 dom 引用或者其他内容。
所以它的类型也有两种。
保存 dom 引用的时候,参数需要传个 null:
不然会报错:
而保存别的内容的时候,不能传 null,不然也会报错,说是 current 只读,这很合理,因为保存的 dom 引用肯定不能改呀。
因为 ref 既可以保存 dom 引用,又可以保存其他数据,而保存 dom 引用又要加上 readonly,所以才用 null 做了个区分。
传 null 就是 dom 引用,返回 RefObject,不传就是其他数据,返回 MutableRefObject。
所以,这就是一种约定,知道传 null 和不传 null 的区别就行了。
useCallback 、useMemo、useContext
useCallback 的类型参数是传入的函数的类型:
useMemo 的类型参数是传入的函数的返回值类型:
useContext 的类型参数是 Context 内容的类型:
当然,这些都没必要手动声明,用默认推导的就行。
EventHandler
很多时候,组件需要传入一些事件处理函数,比如 clickHandler:
import React, { HTMLAttributes, MouseEventHandler } from "react";
interface CccProps {
clickHandler: MouseEventHandler<HTMLDivElement>
}
function Ccc(props: CccProps) {
return <div onClick={props.clickHandler}>ccc</div>
}
function App() {
return <div>
<Ccc clickHandler={(e) => {
console.log(e);
}}></Ccc>
</div>
}
export default App;
这种参数就要用 xxxEventHandler 的类型,比如 MouseEventHandler、ChangeEventHandler 等,它的类型参数是元素的类型。
或者不用 XxxEventHandler,自己声明一个函数类型也可以:
// 鼠标事件
interface CccProps {
clickHandler: (e: MouseEvent<HTMLDivElement>) => void
}
// 输入框change事件
interface CccProps {
clickHandler: (e: ChangeEvent<HTMLInputElement>) => void
}
PropsWithChildren
前面讲过,jsx 类型用 ReactNode,比如这里的 content 参数:
如果你不想通过参数传入内容,可以在 children 里:
这时候就要声明 children 的类型为 ReactNode:
import React, { ReactNode } from "react";
interface CccProps {
content: ReactNode,
children: ReactNode
}
function Ccc(props: CccProps) {
return <div>ccc,{props.content}{props.children}</div>
}
function App() {
return <div>
<Ccc content={<div>666</div>}>
<button>7777</button>
</Ccc>
</div>
}
export default App;
但其实没有必要自己写,传 children 这种情况太常见了,React 提供了相关类型:
type CccProps = PropsWithChildren<{
content: ReactNode,
}>
看下它的类型定义:
就是给 Props 加了一个 children 属性。