【问题标题】:React forwardRef to children prop反应 forwardRef 到儿童道具
【发布时间】:2021-03-30 20:22:09
【问题描述】:

我有以下组件决定是否应根据用户权限呈现组件:

const Can = forwardRef(({ requiredPermissions, children }, ref): null | JSX.Element => {
    const {
        user: { role },
        userPermissions,
    } = useAuth();

    return userHasPermission(role, userPermissions, requiredPermissions) ? children : null;
});

export default Can;

我需要做的是将ref 传递给在使用组件时将在Can 内的子组件。

我怎样才能做到这一点?

【问题讨论】:

  • 我相信你需要使用React.Children API;有关示例,请参见 this SO question
  • 作为一个打字稿问题,您需要确保您传递的 ref 的类型是孩子可以接受的类型。如果孩子是div,那么您必须将引用传递给HTMLDivElement。这很粗糙。如果children 是一个函数,那就更容易了。 <Can>{MyComponent}</Can> 而不是 <Can><MyComponent/></Can> 因为您可以只调用孩子而不是克隆它。 React.children 通常似乎没有正确的 Typescript 类型。
  • 谢谢琳达!我不介意将其转换为函数。您想尝试制定答案吗?我相信这将最接近我想要实现的目标。
  • @Sammy 我正在为如何使Can 通用化而苦苦挣扎。我可以让forwardRefcontents 变得通用,仅此而已。 tsplay.dev/Nl08GN

标签: reactjs typescript react-forwardref


【解决方案1】:

是的,你可以。但是你需要做两件额外的事情:

  1. 您必须确保只有一个孩子被传递给组件。您可以使用React.Children.only 方法进行检查。
  2. 使用React.cloneElementref 传递给孩子。
const Can = forwardRef(({ requiredPermissions, children }, ref): null | JSX.Element => {
    const {
        user: { role },
        userPermissions,
    } = useAuth();

    return userHasPermission(role, userPermissions, requiredPermissions) 
        ? React.cloneElement(React.Children.only(children), {
            ref,
          }) 
        : null;
});

export default Can;

【讨论】:

  • 谢谢,我想了很多,但这就是为什么我问与 Typescript 相关的问题,因为我不断收到此错误:Argument of type 'boolean | {} |反应儿童 |反应门户 |空 | undefined' 不能分配给 'ReactElement>'.
  • 另外,你为什么要在根级别传递...props?不能用元素克隆吗?
  • @Sammy 你是对的。 ... props 不是必需的,但我不知道更广泛的背景,建议您使用它以获得更多“灵活性”。我可以以不同的方式构建答案:) 我修复它。
  • @Sammy children 可以是返回ReactElement 的函数组件,如const Component = () => <p>Hello!</p>ReactElement 直接分配给变量/常量,如const Component = <p>Hello!</p>;React.Children.onlyReact.cloneElement 只接受元素或节点(第二个示例),因此 TypeScript 显示错误。
【解决方案2】:

使用React.ChildrenReact.cloneElement 将ref 属性传递给Can 的所有子元素。

像这样:

const Can = forwardRef(
  ({ requiredPermissions, children }, ref): null | JSX.Element => {
    const {
      user: { role },
      userPermissions,
    } = useAuth();

    const childrenWithProps = () =>
      React.Children.map(children, (child) => {
        if (React.isValidElement(child)) {
          return (
            <React.Fragment>
              {React.cloneElement(child, {
                ref,
              })}
            </React.Fragment>
          );
        }
        return child;
      }) as React.ReactElement;

    return userHasPermission(role, userPermissions, requiredPermissions)
      ? childrenWithProps()
      : null;
  }
);

export default Can;

【讨论】:

  • 谢谢,但这给出了这个错误:TS2322: Type '(() => detailedReactHTMLElement[] | null | undefined) | null' 不可分配给类型 'Element |空值'。输入 '() =>DetailedReactHTMLElement[] |空 | undefined' 缺少类型“元素”的以下属性:类型、道具、键
  • 还有,customRef 是从哪里来的?
  • 尝试用&lt;React.Fragment /&gt; 包装每个孩子,以将其转换为预期的返回类型。它仍然可以只是ref,我使用customRef 只是为了将它与正在传递的ref 区分开来。
  • 另外,我们还应该在克隆之前检查我们映射的每个孩子是否都是有效的React.Element
  • 查看更新答案。让我知道这是否有帮助
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-04-28
  • 1970-01-01
  • 2017-08-03
相关资源
最近更新 更多