【发布时间】:2021-10-18 14:13:04
【问题描述】:
在带有 TypeScript 的 ReactJS 中(在撰写本文时都是最新的稳定版本),我可以定义一个组件,当且仅当给定属性为真时,它才会有条件地呈现其 children。一种可能的实现可能是:
// I will be using this type helper in subsequent examples
export type Maybe<T> = T | undefined | null
export function Only<T>(props: {
when: Maybe<T>
children: ReactNode
}) {
const {when, children} = props
return <>{when ? children : null}</>
}
然后可以按以下方式使用它:
const MyComponent = (props: { value: number }) => (
<div>Value: {props.value}</div>
);
export function MyPage(){
const [value, setValue] = useState<number>()
// value: number | undefined
setTimeout(()=>{
// Simulate a request
setValue(42)
}, 1000)
return (
<div>
<Only when={value}>
<MyComponent value={value!}/>
</Only>
</div>
)
}
它的行为符合预期,但问题是我必须使用非空断言运算符! 才能将value 传递给MyComponent。幸运的是,我可以通过将缩小的类型传递给 children 来解决它:
export function Only<T>(props: {
when: Maybe<T>
children: ReactNode | ((when: T) => ReactNode)
}) {
const {when, children} = props;
return when ?
<>{children instanceof Function ? children(when) : children}</>
: null
}
然后我会以下列方式使用它:
<Only when={value}>{value => // Overshadowing with narrowed type
<MyComponent value={value}/>
}</Only>
再一次,这按预期工作,尽管引入了一点冗长。
但是,如果我需要基于多个属性的“真实性”有条件地渲染一个组件并将它们传递给children,并缩小类型怎么办?一种明显的解决方案是嵌套上述Only 组件:
const MyComponent = (props: { value1: number, value2: string }) => (
<>
<div>Value1: {props.value1}</div>
<div>Value2: {props.value2}</div>
</>
);
export function MyPage() {
const [value1, setValue1] = useState<number>()
const [value2, setValue2] = useState<string>()
setTimeout(() => {
// Simulate a request
setValue1(42)
setValue2('foo')
}, 1000)
return (
<div>
<Only when={value1}>{value1 =>
<Only when={value2}>{value2 =>
<MyComponent value1={value1} value2={value2}/>
}</Only>
}</Only>
</div>
)
}
尽管它可以处理任意数量的属性,但很容易看出这种方法是如何不可扩展的,因为我们需要编写大量样板代码才能以这种方式传递多个属性。理想情况下,我们将在数组中传递这些属性,其成员将以类似的方式缩小。例如,2 个属性的特殊情况如下所示:
export function Only2<T, S>(props: {
when: [Maybe<T>, Maybe<S>]
children: ReactNode | ((when: [T, S]) => ReactNode)
}) {
const {when, children} = props
return when[0] && when[1] ?
<>{children instanceof Function ? children([when[0], when[1]]) : children}</>
: null
}
然后它将以下列方式使用:
<Only2 when={[value1, value2]}>{([value1, value2]) =>
<MyComponent value1={value1} value2={value2}/>
}</Only2>
不幸的是,我无法为一般情况提出解决方案,在这种情况下,我们能够以方便的方式传递和缩小任意数量的值的类型,如上面的 sn-p 所示。因此我的问题是双重的:是否有可能实现我在 TypeScript 中寻找的结果?如果没有,是否有任何替代策略来实现可以以方便的方式使用的通用组件,如使用Only 组件的 sn-ps 中所示?
【问题讨论】:
标签: reactjs typescript typescript-generics