一、对tsx语法的介绍
JSX是JavaScript语法的扩展,它允许我们在JavaScript中直接编写类似于XML的代码,用于描述用户界面和HTML元素。而TSX则是基于JSX的扩展,允许我们在TypeScript中编写JSX语法的代码。TSX可以让我们在编写React组件时,使用TypeScript的类型检查来避免一些常见的错误,在编译时就发现这些错误。
在TSX中,我们可以直接在TypeScript代码中编写类似于HTML的元素,比如div、p、button等,像下面这样:
function HelloWorld(props: {name: string}) { returnHello {props.name}!; }
上面的代码中,我们定义了一个名为HelloWorld的函数组件,它接受一个props参数,其中包含一个名为name的字符串属性。在函数的返回值中,我们使用了一个div元素,其中包含了一个Hello和props.name的字符串插值表达式,最后返回这个div元素。
二、TSX的最佳实践
1. 使用接口声明组件的props
在定义React组件时,我们经常会使用props对象传递组件的属性,也就是一些配置信息和状态数据。为了避免在使用props时出现一些类型错误,我们可以使用接口来声明组件的props类型。像下面这样:
interface Props { name: string; age: number; onClick?: () => void; } function MyComponent(props: Props) { return (Name: {props.name}); }
Age: {props.age}
{props.onClick && }
上面的代码中,我们定义了一个名为Props的接口,它包含了三个属性:name、age和一个可选的onClick回调函数。在MyComponent函数组件中,我们使用Props接口来声明了props的类型,然后在函数的返回值中使用了三个插值表达式来渲染props的值。
2. 使用枚举类型定义常量值
在编写React组件时,我们经常会定义一些常量值,比如组件的状态、类型、样式等等。为了避免在使用常量值时出现一些错误,我们可以使用枚举类型来定义常量值,并在使用时直接引用枚举项的名称。像下面这样:
enum Status { Draft = 'draft', Published = 'published', Archived = 'archived' } interface Props { status: Status; } function MyComponent(props: Props) { returnStatus: {props.status}; } function App() { return ( <>); }
上面的代码中,我们定义了一个名为Status的枚举类型,它包含了三个枚举项:Draft、Published和Archived。在MyComponent组件中,我们使用了Status枚举类型来声明了status属性的类型,并在函数的返回值中直接渲染了props.status的值。在App组件中,我们使用了MyComponent组件,并在props中传递了不同的Status枚举项。
3. 使用泛型类型定义函数组件
在编写React组件时,我们经常会定义一些通用的组件,比如列表、表单、对话框等等。为了使这些组件更加灵活和复用,我们可以使用泛型类型来定义函数组件,并在使用时传入不同的类型参数。像下面这样:
interface Item { id: string; name: string; } interface Props{ items: T[]; renderItem: (item: T) => React.ReactNode; } function List (props: Props ) { return ( {props.items.map(item => (
); } function App() { const items: Item[] = [ {id: '1', name: 'Apple'}, {id: '2', name: 'Banana'}, {id: '3', name: 'Orange'} ]; return (- {props.renderItem(item)}
))}{item.name}} /> ); }
上面的代码中,我们定义了一个名为Item的接口,它包含了两个属性:id和name。在Props接口中,我们使用了泛型类型T来定义了items和renderItem属性的类型,并在List函数组件中使用了T来表示不同的类型参数。在函数的返回值中,我们通过map方法遍历items数组,并渲染了每一个item和相应的renderItem函数。在App组件中,我们定义了一个名为items的数组,并在props中传递了List组件和renderItem函数。
三、TSX的技巧
1. 使用Fragment代替外部div包裹
在编写React组件时,我们经常需要在JSX中使用多个组件或元素,但又不想用一个外部div元素包裹它们。为了解决这个问题,我们可以使用React提供的Fragment组件来包裹JSX中的多个元素。像下面这样:
function MyComponent() { return ( <>Title
Paragraph
); }
上面的代码中,我们使用了React提供的Fragment组件来包裹了h1和p元素,实现了多个元素的无父级元素包裹。
2. 使用TypeScript的类型别名
在编写React组件时,我们经常需要使用复杂的类型来描述组件的props属性,比如联合类型、交叉类型、可选属性等等。为了避免代码重复,在TypeScript中我们可以使用类型别名来定义这些复杂的类型,并在组件中重复使用。像下面这样:
type User = { id: string; name: string; age: number; } type Status = 'draft' | 'published' | 'archived'; type Props = { user: User; status?: Status; } function UserCard(props: Props) { return (); }{props.user.name} ({props.user.age})
{props.user.id}
{props.status &&Status: {props.status}
}
上面的代码中,我们使用了类型别名来定义了User、Status和Props三个类型,分别表示用户对象、状态值和组件的props属性。在UserCard组件中,我们使用Props来声明了props属性的类型,并在函数的返回值中渲染了props属性的值。
3. 使用React.forwardRef传递ref
在编写React组件时,我们经常需要在组件内部使用ref来操作DOM元素或子组件。但是,有些时候我们需要将ref传递给组件的子组件,而传递ref的方式却并不那么简单。为了解决这个问题,React提供了forwardRef方法来传递ref。像下面这样:
interface Props { children: React.ReactNode; } const InnerComponent = React.forwardRef((props, ref) => ( {props.children})); function MyComponent() { const ref = useRef(null); useEffect(() => { if (ref.current) { console.log(ref.current.getBoundingClientRect()); } }, []); return ( ); } Title
Paragraph
上面的代码中,我们使用了React.forwardRef方法来创建了一个名为InnerComponent的函数组件,并在组件中定义了ref属性的类型。在MyComponent组件中,我们使用了useRef钩子来创建了一个类型为HTMLDivElement的ref,并在InnerComponent组件中传递了这个ref。在组件渲染后,我们使用useEffect钩子来操作ref.current属性,并输出了DOM元素的边界矩形信息。