[英]Using ref with React.createElement
I've got a reusable heading component that allows me to pass a tag
prop, creating any sort of heading (h1, h2, h3 etc).我有一个可重用的标题组件,它允许我传递一个
tag
道具,创建任何类型的标题(h1、h2、h3 等)。 Here's that component:这是那个组件:
heading.tsx标题.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 coming 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.但是,我遇到了一个用例,我希望能够在
Tag
上使用useRef()
挂钩来使用ref
,这样我就可以访问元素并使用 GSAP 进行动画处理。 However, I can't figure out how do this using createElement
.但是,我无法弄清楚如何使用
createElement
来做到这一点。
I've tried to do it by adding a ref
directly to the Tag
component, and adding it to the Tag
props like so:我尝试通过将
ref
直接添加到Tag
组件并将其添加到Tag
道具中来做到这一点,如下所示:
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>'.
我收到错误
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 component?我做错了什么,如何安全地向组件添加
ref
?
Thanks.谢谢。
You need to forward ref and then pass it not as a child or element, but as it's property.您需要转发 ref,然后将其作为子元素或元素传递,而是作为其属性传递。
Here is documentation on ref forwarding: https://reactjs.org/docs/forwarding-refs.html这是关于参考转发的文档: https://reactjs.org/docs/forwarding-refs.html
Here is code exapmles for heading without intermediate component 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, ref) => {
return (
<Tag className={s(s.heading, className)} ref={headingRef}>
{children}
</Tag>
);
}
);
export const HeadingWithoutJSX = forwardRef(
({ tag, children, className }: HeadingProps, ref) => {
return createElement(
tag,
{ className: s(s.heading, className), ref },
children
);
}
);
Use object spread to add the ref
to the props
:使用 object spread 将
ref
添加到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.com/react@17/umd/react.development.js"></script> <script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script> <div id="root"></div>
However, creating a component inside another component would mean that component would be recreated on each render.但是,在另一个组件中创建一个组件意味着该组件将在每次渲染时重新创建。 You can avoid that by using
useMemo()
.您可以通过使用
useMemo()
来避免这种情况。 However an easier option would be to render the tag
itself as JSX:然而,更简单的选择是将
tag
本身呈现为 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.com/react@17/umd/react.development.js"></script> <script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script> <div id="root"></div>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.