如何克隆 React.Node 类型?

时间:2021-02-09 20:40:58

标签: reactjs typescript

我有以下代码,但无法编译:

import React, {PropsWithChildren} from "react";
import useScrollTrigger from "@material-ui/core/useScrollTrigger";


export default function ElevationScroll({children}: PropsWithChildren<{}>) {

    const trigger = useScrollTrigger({
        disableHysteresis: true,
        threshold: 0
    });

    return React.cloneElement(children, {
        elevation: trigger ? 3 : 0,
    });

错误信息:

  The last overload gave the following error.
    Argument of type 'ReactNode' is not assignable to parameter of type 'ReactElement<any, string | ((props: any) => ReactElement<any, any> | null) | (new (props: any) => Component<any, any, any>)>'.
      Type 'undefined' is not assignable to type 'ReactElement<any, string | ((props: any) => ReactElement<any, any> | null) | (new (props: any) => Component<any, any, any>)>'.  TS2769

    10 |     });
    11 | 
  > 12 |     return React.cloneElement(children, {
       |                               ^
    13 |         elevation: trigger ? 3 : 0,
    14 |     });
    15 | }

ReactNode的类型定义:

type ReactChild = ReactElement | ReactText;
type ReactNode = ReactChild | ReactFragment | ReactPortal | boolean | null | undefined;

ReactNode 也可以是 ReactElement
但是为什么 React.cloneElement 不接受 children

更新

我将如何使用 ElevationScroll

<ElevationScroll>
    <AppBar color="default" elevation={0} classes={{colorDefault: classes.appBar}}>
        <Container maxWidth="lg">
            <Toolbar className={classes.toolbar}>
                <img alt="logo" src={logo} className={classes.logo}/>
            </Toolbar>
        </Container>
    </AppBar>
</ElevationScroll>

如您所见,它只包含一个孩子。

我更改了 ElevationScroll 的实现:

export default function ElevationScroll({children}: PropsWithChildren<{}>) {

    const trigger = useScrollTrigger({
        disableHysteresis: true,
        threshold: 0
    });


    return React.cloneElement(React.Children.only(children), {
        elevation: trigger ? 3 : 0,
    });
}

编译器仍然抱怨:

No overload matches this call.
  The last overload gave the following error.
    Argument of type 'string | number | boolean | {} | ReactElement<any, string | ((props: any) => ReactElement<any, any> | null) | (new (props: any) => Component<any, any, any>)> | ReactPortal | null | undefined' is not assignable to parameter of type 'ReactElement<any, string | ((props: any) => ReactElement<any, any> | null) | (new (props: any) => Component<any, any, any>)>'.
      Type 'undefined' is not assignable to type 'ReactElement<any, string | ((props: any) => ReactElement<any, any> | null) | (new (props: any) => Component<any, any, any>)>'.  TS2769

    11 | 
    12 | 
  > 13 |     return React.cloneElement(React.Children.only(children), {
       |                               ^
    14 |         elevation: trigger ? 3 : 0,
    15 |     });
    16 | }

我也试过:

export default function ElevationScroll({children}: PropsWithChildren<{}>) {

    const trigger = useScrollTrigger({
        disableHysteresis: true,
        threshold: 0
    });


    return React.Children
        .map(children, (ele) => React.cloneElement(ele, {
            elevation: trigger ? 3 : 0,
        }));
}

编译器抱怨:

  The last overload gave the following error.
    Argument of type 'ReactNode' is not assignable to parameter of type 'ReactElement<any, string | ((props: any) => ReactElement<any, any> | null) | (new (props: any) => Component<any, any, any>)>'.
      Type 'undefined' is not assignable to type 'ReactElement<any, string | ((props: any) => ReactElement<any, any> | null) | (new (props: any) => Component<any, any, any>)>'.  TS2769

    12 | 
    13 |     return React.Children
  > 14 |         .map(children, (ele) => React.cloneElement(ele, {
       |                                                    ^
    15 |             elevation: trigger ? 3 : 0,
    16 |         }));
    17 | }

1 个答案:

答案 0 :(得分:2)

您不能提供 ReactNodechildren 类型)作为 ReactElement。问题是 ReactNode 还包括片段、普通文本和其他原始值。你必须事先施放它(不太好)或(更好)仔细检查你的孩子

如果您只想抚养一个孩子 - 使用 Children.only。 如果您想克隆多个元素 - 使用 Children.map

如果你想支持片段,你还应该检查给定的元素是否是一个片段,然后做任何你想做的事情——忽略它,改用它的子元素。

在此处阅读有关 Children 实用程序的更多信息:https://reactjs.org/docs/react-api.html#reactchildren