reactjs - 如何在保持类型非可选的同时抽象传递重复的子属性?

标签 reactjs typescript

我有一个连续多次使用的组件,它具有一些相同的属性和一些独特的属性:

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"; }'.

我想到的唯一解决方案是将 InsideComponentrepeatedThing 属性标记为可选,但这并不理想,因为值需要。

我如何保持严格性以确保 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 an OutsideComponentProps 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)和 oT因为否则编译器会考虑 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/

相关文章:

reactjs - 使用钩子(Hook)从数组中删除对象(useState)

javascript - 无法从字符串访问 this.props 到函数

javascript - 如何在同一事件中调用 typescript 箭头函数和javascript函数

javascript - 在 react 中渲染二维数组的最佳方法?

javascript - 400(错误请求)并且找不到manifest.json

当数字有 3 位小数并固定为 2 位小数时,Javascript number.toFixed() 不起作用

meteor - 如何使用 TypeScript 构建应用程序?

javascript - 防止使用 Observable Angular 2 时插入重复项

javascript - Angular 2注入(inject)全局依赖

reactjs - 向我的 React 应用程序添加字体很棒的图标