javascript - Using ref with React.createElement - Stack Overflow

I've got a reusable heading ponent that allows me to pass a tag prop, creating any sort of heading

I've got a reusable heading ponent that allows me to pass a tag prop, creating any sort of heading (h1, h2, h3 etc). Here's that ponent:

heading.tsx

import React, { ReactNode } from 'react';

import s from './Heading.scss';

interface HeadingProps {
  tag: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
  children: ReactNode;
  className?: string;
}

export const Heading = ({ tag, children, className }: HeadingProps) => {
  const Tag = ({ ...props }: React.HTMLAttributes<HTMLHeadingElement>) =>
    React.createElement(tag, props, children);

  return <Tag className={s(s.heading, className)}>{children}</Tag>;
};

However, I'm ing across a use case where I'd like to be able to have a ref, using the useRef() hook, on the Tag, so that I can access the element and animate with GSAP. However, I can't figure out how do this using createElement.

I've tried to do it by adding a ref directly to the Tag ponent, and adding it to the Tag props like so:

import React, { ReactNode, useRef } from 'react';

import s from './Heading.scss';

interface HeadingProps {
  tag: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
  children: ReactNode;
  className?: string;
}

export const Heading = ({ tag, children, className }: HeadingProps) => {
  const headingRef = useRef(null);
  const Tag = ({ ...props }: React.HTMLAttributes<HTMLHeadingElement>) =>
    React.createElement(tag, props, children, {ref: headingRef});

  return <Tag className={s(s.heading, className)} ref={headingRef}>{children}</Tag>;
};

I receive the error Property 'ref' does not exist on type 'IntrinsicAttributes & HTMLAttributes<HTMLHeadingElement>'.

What am I doing wrong, and how can I safely add a ref to the ponent?

Thanks.

I've got a reusable heading ponent that allows me to pass a tag prop, creating any sort of heading (h1, h2, h3 etc). Here's that ponent:

heading.tsx

import React, { ReactNode } from 'react';

import s from './Heading.scss';

interface HeadingProps {
  tag: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
  children: ReactNode;
  className?: string;
}

export const Heading = ({ tag, children, className }: HeadingProps) => {
  const Tag = ({ ...props }: React.HTMLAttributes<HTMLHeadingElement>) =>
    React.createElement(tag, props, children);

  return <Tag className={s(s.heading, className)}>{children}</Tag>;
};

However, I'm ing across a use case where I'd like to be able to have a ref, using the useRef() hook, on the Tag, so that I can access the element and animate with GSAP. However, I can't figure out how do this using createElement.

I've tried to do it by adding a ref directly to the Tag ponent, and adding it to the Tag props like so:

import React, { ReactNode, useRef } from 'react';

import s from './Heading.scss';

interface HeadingProps {
  tag: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
  children: ReactNode;
  className?: string;
}

export const Heading = ({ tag, children, className }: HeadingProps) => {
  const headingRef = useRef(null);
  const Tag = ({ ...props }: React.HTMLAttributes<HTMLHeadingElement>) =>
    React.createElement(tag, props, children, {ref: headingRef});

  return <Tag className={s(s.heading, className)} ref={headingRef}>{children}</Tag>;
};

I receive the error Property 'ref' does not exist on type 'IntrinsicAttributes & HTMLAttributes<HTMLHeadingElement>'.

What am I doing wrong, and how can I safely add a ref to the ponent?

Thanks.

Share Improve this question asked Mar 9, 2021 at 15:22 Jesse WintonJesse Winton 71617 silver badges46 bronze badges
Add a ment  | 

2 Answers 2

Reset to default 4

Use object spread to add the ref to the props:

const { useRef, useEffect } = React;

const Heading = ({ tag, children, className }) => {
  const headingRef = useRef(null);
  const Tag = (props) => React.createElement(tag, {ref: headingRef, ...props }, children);
  
  useEffect(() => { console.log(headingRef); }, []); // demo - use the ref

  return <Tag>{children}</Tag>;
};

ReactDOM.render(
  <div>
    <Heading tag="h1">h1</Heading>
    <Heading tag="h2">h2</Heading>
    <Heading tag="h3">h3</Heading>
  </div>,
  root
);
.as-console-wrapper { max-height: 100% !important; top: 0; left: 50% !important; }
<script crossorigin src="https://unpkg./react@17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg./react-dom@17/umd/react-dom.development.js"></script>

<div id="root"></div>

However, creating a ponent inside another ponent would mean that ponent would be recreated on each render. You can avoid that by using useMemo(). However an easier option would be to render the tag itself as JSX:

const { useRef, useEffect } = React;

const Heading = ({ tag: Tag, children, className }) => {
  const headingRef = useRef(null);
  
  useEffect(() => { console.log(headingRef); }, []); // demo - use the ref

  return <Tag className={className} ref={headingRef}>{children}</Tag>;
};

ReactDOM.render(
  <div>
    <Heading tag="h1">h1</Heading>
    <Heading tag="h2">h2</Heading>
    <Heading tag="h3">h3</Heading>
  </div>,
  root
);
.as-console-wrapper { max-height: 100% !important; top: 0; left: 50% !important; }
<script crossorigin src="https://unpkg./react@17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg./react-dom@17/umd/react-dom.development.js"></script>

<div id="root"></div>

You need to forward ref and then pass it not as a child or element, but as it's property.

Here is documentation on ref forwarding: https://reactjs/docs/forwarding-refs.html

Here is code exapmles for heading without intermediate ponent creation:

import React, { ReactNode, useRef } from "react";

import s from "./Heading.scss";

interface HeadingProps {
  tag: "h1" | "h2" | "h3" | "h4" | "h5" | "h6";
  children: ReactNode;
  className?: string;
}

export const Heading = forwardRef(
  ({ tag: Tag, children, className }: HeadingProps, headingRef) => {
    return (
      <Tag className={s(s.heading, className)} ref={headingRef}>
        {children}
      </Tag>
    );
  }
);

export const HeadingWithoutJSX = forwardRef(
  ({ tag, children, className }: HeadingProps, headingRef) => {
    return createElement(
      tag,
      { className: s(s.heading, className), ref: headingRef},
      children
    );
  }
);

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744529766a4579074.html

相关推荐

  • javascript - Using ref with React.createElement - Stack Overflow

    I've got a reusable heading ponent that allows me to pass a tag prop, creating any sort of heading

    1天前
    10

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信