我有一个连续多次使用的组件,它具有一些相同的属性和一些独特的属性:
interface InsideComponentProps {
repeatedThing: string;
uniqueThing: string;
}
const InsideComponent: React.SFC<InsideComponentProps> = ({ repeatedThing, uniqueThing }) => (
<div>{repeatedThing} - {uniqueThing}</div>
);
const Example = () => (
<div>
<InsideComponent repeatedThing="foo" uniqueThing="1" />
<InsideComponent repeatedThing="foo" uniqueThing="2" />
<InsideComponent repeatedThing="foo" uniqueThing="3" />
</div>
);
重复的 repeatedThing
属性困扰着我,所以我正在寻找一种方法来消除这种冗余。我在非 TypeScript 应用程序中做过的一件事是引入一个包装器组件来克隆所有子项,并在此过程中添加重复的属性:
interface OutsideComponentProps {
repeatedThing: string;
}
const OutsideComponent: React.SFC<OutsideComponentProps> = ({ repeatedThing, children }) => (
<div>
{React.Children.map(children, (c: React.ReactElement<any>) => (
React.cloneElement(c, { repeatedThing })
))}
</div>
);
const Example = () => (
<OutsideComponent repeatedThing="foo">
<InsideComponent uniqueThing="1" />
<InsideComponent uniqueThing="2" />
<InsideComponent uniqueThing="3" />
</OutsideComponent>
);
生成的 JavaScript 代码具有我想要的行为,但 TypeScript 编译器有错误,因为我在实例化 InsideComponent
时没有传递所有必需的属性:
ERROR in [at-loader] ./src/index.tsx:27:26
TS2322: Type '{ uniqueThing: "1"; }' is not assignable to type 'IntrinsicAttributes & InsideComponentProps & { children?: ReactNode; }'.
Type '{ uniqueThing: "1"; }' is not assignable to type 'InsideComponentProps'.
Property 'repeatedThing' is missing in type '{ uniqueThing: "1"; }'.
我想到的唯一解决方案是将 InsideComponent
的 repeatedThing
属性标记为可选,但这并不理想,因为值是需要。
我如何保持严格性以确保 InsideComponent
确实接收到所有 props,同时减少调用站点属性的重复?
我正在使用 React 16.2.0 和 TypeScript 2.6.2。
最佳答案
TypeScript 检查以确保您将所有必需的属性分配给 React 元素。由于您在 OutsideComponent
中分配了额外的属性编译器无法真正检查。
一个选项是将子项指定为一个函数,该函数将额外的属性作为参数并将它们传播到 InsideComponent
.语法有点复杂,但类型更安全:
interface OutsideComponentProps {
repeatedThing: string;
children: (outerProps: OutsideComponentProps) => React.ReactElement<any>;
}
const OutsideComponent: React.SFC<OutsideComponentProps> = (o) => o.children(o);
const Example = () => (
<OutsideComponent repeatedThing="foo">{(o) =>
<div>
<InsideComponent uniqueThing="1" {...o} />
<InsideComponent uniqueThing="2" {...o} />
<InsideComponent uniqueThing="3" {...o} />
</div>
}</OutsideComponent>
);
It seems that
OutsideComponent
is very abstract and thus reusable; is there any way to convert it into a very generic component that takes all of its props and provides them as the argument, without having to define anOutsideComponentProps
for each case?
虽然您可以将泛型函数用作组件,但您不能显式指定类型参数,它们只能被推断。这是一个缺点,但最终可以解决。
function GenericOutsideComponent<T>(props: { children: (o: T) => React.ReactElement<any> } & Partial<T>, context?: any): React.ReactElement<any> {
return props.children(props as any);
}
const Example = () => (
<GenericOutsideComponent repeatedThing="foo">{(o: InsideComponentProps) =>
<div>
<InsideComponent uniqueThing="1" {...o} />
<InsideComponent uniqueThing="2" {...o} />
<InsideComponent uniqueThing="3" {...o} />
</div>
}</GenericOutsideComponent>
);
与您的原始 JavaScript 解决方案类似,存在 InsideComponent
的某些必需属性存在风险未指定为 GenericOutsideComponent
的属性是 Partial<T>
(只允许指定 repeatedThing
)和 o
是T
因为否则编译器会考虑 repeatedThing
未指定,将在 InsideComponent
上需要它.如果在 InsideComponent
上设置虚拟值没问题,只需更改children
的签名即可至 (o: Partial<T>) => React.ReactElement<any>
但这不太理想。
另一种选择是明确哪些属性在 GenericOutsideComponent
上使用 Pick
:
function GenericOutsideComponent<T>(props: { children: (o: T) => React.ReactElement<any> } & T, context?: any): React.ReactElement<any> {
return props.children(props);
}
const Example = () => (
<GenericOutsideComponent repeatedThing="foo">{(o: Pick<InsideComponentProps, "repeatedThing">) =>
<div>
<InsideComponent uniqueThing="1" {...o} />
<InsideComponent uniqueThing="2" {...o} />
<InsideComponent uniqueThing="3" {...o} />
</div>
}</GenericOutsideComponent>
);
关于reactjs - 如何在保持类型非可选的同时抽象传递重复的子属性?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48435346/