【问题标题】:How to clone React.Node type?如何克隆 React.Node 类型?
【发布时间】:2021-02-09 20:40:58
【问题描述】:

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

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 | }

【问题讨论】:

标签: reactjs typescript


【解决方案1】:

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

如果您只想支持独生子女 - 请使用Children.only。 如果你想克隆多个元素 - 使用Children.map

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

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

【讨论】:

  • 对于Children.only,Typescript 的定义似乎有点奇怪,因为这个函数忽略了原语。我创建了一个带有示例here 的沙箱。如您所见,我将children 重新定义为ReactElement。只要你确定这个组件应该只允许提供一个 Reactish 孩子就可以了。第二种方法是使用isValidElement 100% 确定或只是转换从Children.only 返回的值。
  • primitives 是什么意思?
猜你喜欢
  • 2011-01-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-16
  • 1970-01-01
  • 2018-03-26
  • 2018-07-24
  • 2014-01-09
相关资源
最近更新 更多