isValidElement
官方解释
判断是不是 React 元素:只有通过 createElement 返回的 JSX 标签 和 对象 会被认为是 React 元素。)
例如,即使像
42
这样的数字是一个有效的 React 节点(并且能从一个组件中返回),但它仍然不是有效的 React 元素。使用createPortal
创建的数组和 portal 也 不被 认为是 React 元素。
// JSX
console.log(isValidElement(<p />)); // true
console.log(isValidElement(<MyComponent />)); // true
// createElement 创建的
console.log(isValidElement(createElement('p'))); // true
console.log(isValidElement(createElement(MyComponent))); // true
console.log(isValidElement(null)); // false
console.log(isValidElement(25)); // false
console.log(isValidElement('Hello')); // false
console.log(isValidElement({ age: 42 })); // false
console.log(isValidElement([<div />, <div />])); // false
console.log(isValidElement(MyComponent)); // false
场景
需要明确传入的参数必须是可作为组件的 react 元素,比如下面是一个简单的案例:常见的 table 组件中会有大量的筛选组件,这里的 appliedfilters
一个包含多个筛选组件对象的数组:
export interface AppliedFilters {
key: string
filter: ReactNode
}
const appliedfilters: AppliedFilters[] = []
if (table.getColumn("status")) {
appliedfilters.push({
key: "status",
filter: (
<FilterCheckbox
column={table.getColumn("status")}
title="状态"
options={statusOptions}
/>
),
})
}
渲染每一个 filter
需要确认时 react 元素
{filters.map((f) => {
if (isValidElement(f.filter)) {
const Comp = f.filter
return <Fragment key={f.key}>{Comp}</Fragment>
}
// other code
})}
关于 filter 类型声明 ReactNode 保证其可扩展性和灵活性;当然 filter 还可能有很多种情况,所声明基础类型接口的时候,泛用性尽可能的好,然后在具体组件可以进一步约束类型。
这对于维护和开发个人认为是一个不错的方式。当然技术没有银弹,方案也不是绝对的,这里仅仅作为参考
React 中的常用类型
props 类型的声明
常见的也是最直观简单的方式是给函数组件的 props 直接声明每一个属性的类型,比如下面封装了一个简单的基础组件 Show ,它接收三个属性,这里可以分别定义:
import React from "react"
interface ShowProps {
when?: boolean
fallback?: React.ReactNode
children?: React.ReactNode
}
export const Show = ({ when, fallback, children }: ShowProps) => {
return when ? <>{children}</> : <>{fallback}</>
}
PropsWithChildren
在上面的组件中甚至日常封装的众多组件都包含了 children
属性,反复声明让代码有点冗余,当声明的类型足够多的时候,是否含有 children 属性就略显麻烦,所以 react 提供了一个工具类 PropsWithChildren
:
type PropsWithChildren<P = unknown> = P & { children?: ReactNode | undefined };
现在改进一下 Show 组件可以如下样子:
import React, { PropsWithChildren } from "react"
interface ShowProps {
when?: boolean
fallback?: React.ReactNode
}
export const Show = ({ when, fallback, children }: PropsWithChildren<ShowProps>) => {
return when ? <>{children}</> : <>{fallback}</>
}
现在看 ShowProps 的类型就更加有灵活性,而且也不用每次都需要声明 children 类型了。
如果是一个容器组件有且只有一个属性 children ,可以直接声明 PropsWithChildren 而不需要传递类型参数。
…props 剩余传参
有时我们希望将所有剩余的 props 全部传递到某些底层组件中,除了组件,在原生的 html 元素中这种方式也有效。
import { ComponentProps } from 'react'
import ProductTile from './ProductTile'
type Props = {
color: string
} & ComponentProps<typeof ProductTile>
function ProminentProductTile({ color, ...props }: Props) {
return (
<div style={{ background: color }}>
<ProductTile {...props} />
</div>
)
}
更多阅读
关于 useState、useReducer、useRef 和常见的事件类型 Robin Weser | Clean React with TypeScript