将 ref 与 React.createElement 一起使用
Using ref with React.createElement
我有一个可重复使用的标题组件,它允许我传递 tag
道具,创建任何类型的标题(h1、h2、h3 等)。这是该组件:
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>;
};
但是,我遇到了一个用例,我希望能够在 Tag
上使用 useRef()
挂钩 ref
,所以我可以访问该元素并使用 GSAP 制作动画。但是,我不知道如何使用 createElement
.
我尝试通过将 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>;
};
我收到错误 Property 'ref' does not exist on type 'IntrinsicAttributes & HTMLAttributes<HTMLHeadingElement>'.
我哪里做错了,我怎样才能安全地将 ref
添加到组件?
谢谢。
您需要转发 ref 然后将其作为 child 或元素传递,而是作为 属性.
传递
这是关于 ref 转发的文档:https://reactjs.org/docs/forwarding-refs.html
这里是没有创建中间组件的标题的代码示例:
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
);
}
);
使用对象传播将 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>
但是,在另一个组件内创建一个组件意味着将在每次渲染时重新创建该组件。您可以使用 useMemo()
来避免这种情况。然而,一个更简单的选择是将 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>
我有一个可重复使用的标题组件,它允许我传递 tag
道具,创建任何类型的标题(h1、h2、h3 等)。这是该组件:
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>;
};
但是,我遇到了一个用例,我希望能够在 Tag
上使用 useRef()
挂钩 ref
,所以我可以访问该元素并使用 GSAP 制作动画。但是,我不知道如何使用 createElement
.
我尝试通过将 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>;
};
我收到错误 Property 'ref' does not exist on type 'IntrinsicAttributes & HTMLAttributes<HTMLHeadingElement>'.
我哪里做错了,我怎样才能安全地将 ref
添加到组件?
谢谢。
您需要转发 ref 然后将其作为 child 或元素传递,而是作为 属性.
传递这是关于 ref 转发的文档:https://reactjs.org/docs/forwarding-refs.html
这里是没有创建中间组件的标题的代码示例:
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
);
}
);
使用对象传播将 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>
但是,在另一个组件内创建一个组件意味着将在每次渲染时重新创建该组件。您可以使用 useMemo()
来避免这种情况。然而,一个更简单的选择是将 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>