-
-
Notifications
You must be signed in to change notification settings - Fork 117
feat: add ref forwarding to internal img element. Closes #372 #379
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 1 commit
b88ff56
0b6f315
338cb86
f8720c3
14a74e3
26a8205
b04e15e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -3,7 +3,7 @@ import type { GetContainer } from '@rc-component/util/lib/PortalWrapper'; | |||||||||||||||||||||||||||||||||||||||
| import useMergedState from '@rc-component/util/lib/hooks/useMergedState'; | ||||||||||||||||||||||||||||||||||||||||
| import cn from 'classnames'; | ||||||||||||||||||||||||||||||||||||||||
| import * as React from 'react'; | ||||||||||||||||||||||||||||||||||||||||
| import { useContext, useMemo, useState } from 'react'; | ||||||||||||||||||||||||||||||||||||||||
| import { useContext, useMemo, useState, forwardRef } from 'react'; | ||||||||||||||||||||||||||||||||||||||||
| import type { PreviewProps, ToolbarRenderInfoType } from './Preview'; | ||||||||||||||||||||||||||||||||||||||||
| import Preview from './Preview'; | ||||||||||||||||||||||||||||||||||||||||
| import PreviewGroup from './PreviewGroup'; | ||||||||||||||||||||||||||||||||||||||||
|
|
@@ -69,11 +69,11 @@ export interface ImageProps | |||||||||||||||||||||||||||||||||||||||
| onError?: (e: React.SyntheticEvent<HTMLImageElement, Event>) => void; | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| interface CompoundedComponent<P> extends React.FC<P> { | ||||||||||||||||||||||||||||||||||||||||
| interface CompoundedComponent<P> extends React.ForwardRefExoticComponent<P & React.RefAttributes<HTMLImageElement>> { | ||||||||||||||||||||||||||||||||||||||||
| PreviewGroup: typeof PreviewGroup; | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| const ImageInternal: CompoundedComponent<ImageProps> = props => { | ||||||||||||||||||||||||||||||||||||||||
| const ImageInternal = forwardRef<HTMLImageElement, ImageProps>((props, ref) => { | ||||||||||||||||||||||||||||||||||||||||
| const { | ||||||||||||||||||||||||||||||||||||||||
| src: imgSrc, | ||||||||||||||||||||||||||||||||||||||||
| alt, | ||||||||||||||||||||||||||||||||||||||||
|
|
@@ -180,6 +180,22 @@ const ImageInternal: CompoundedComponent<ImageProps> = props => { | |||||||||||||||||||||||||||||||||||||||
| onClick?.(e); | ||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| // ========================== Combined Ref ========================== | ||||||||||||||||||||||||||||||||||||||||
| const handleRef = (img: HTMLImageElement | null) => { | ||||||||||||||||||||||||||||||||||||||||
| if (img) { | ||||||||||||||||||||||||||||||||||||||||
| getImgRef(img); | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| // 处理外部传入的 ref | ||||||||||||||||||||||||||||||||||||||||
| if (ref) { | ||||||||||||||||||||||||||||||||||||||||
| if (typeof ref === 'function') { | ||||||||||||||||||||||||||||||||||||||||
| ref(img); | ||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||
| ref.current = img; | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+208
to
+216
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion 集成内部和外部引用的处理函数
const handleRef = (img: HTMLImageElement | null) => {
if (!img) {
+ imageElementRef.current = null;
return;
}
// 保存到内部引用
imageElementRef.current = img;
getImgRef(img);
};📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| // =========================== Render =========================== | ||||||||||||||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||||||||||||||
| <> | ||||||||||||||||||||||||||||||||||||||||
|
|
@@ -206,7 +222,7 @@ const ImageInternal: CompoundedComponent<ImageProps> = props => { | |||||||||||||||||||||||||||||||||||||||
| height, | ||||||||||||||||||||||||||||||||||||||||
| ...style, | ||||||||||||||||||||||||||||||||||||||||
| }} | ||||||||||||||||||||||||||||||||||||||||
| ref={getImgRef} | ||||||||||||||||||||||||||||||||||||||||
| ref={handleRef} | ||||||||||||||||||||||||||||||||||||||||
| {...srcAndOnload} | ||||||||||||||||||||||||||||||||||||||||
| width={width} | ||||||||||||||||||||||||||||||||||||||||
| height={height} | ||||||||||||||||||||||||||||||||||||||||
|
|
@@ -257,8 +273,7 @@ const ImageInternal: CompoundedComponent<ImageProps> = props => { | |||||||||||||||||||||||||||||||||||||||
| )} | ||||||||||||||||||||||||||||||||||||||||
| </> | ||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| }) as CompoundedComponent<ImageProps>; | ||||||||||||||||||||||||||||||||||||||||
| ImageInternal.PreviewGroup = PreviewGroup; | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| if (process.env.NODE_ENV !== 'production') { | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,89 @@ | ||
| import { act, fireEvent, render } from '@testing-library/react'; | ||
| import React from 'react'; | ||
| import Image from '../src'; | ||
|
|
||
| describe('Image ref forwarding', () => { | ||
| // 测试对象类型的 ref | ||
| it('should forward object ref to internal img element', () => { | ||
| const ref = React.createRef<HTMLImageElement>(); | ||
| const { container } = render( | ||
| <Image | ||
| ref={ref} | ||
| src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png" | ||
| alt="test image" | ||
| />, | ||
| ); | ||
|
|
||
| // 确保 ref.current 指向正确的 img 元素 | ||
| expect(ref.current).not.toBeNull(); | ||
| expect(ref.current).toBe(container.querySelector('.rc-image-img')); | ||
| expect(ref.current?.tagName).toBe('IMG'); | ||
| expect(ref.current?.alt).toBe('test image'); | ||
| }); | ||
|
|
||
| // 测试回调类型的 ref | ||
| it('should work with callback ref', () => { | ||
| let imgElement: HTMLImageElement | null = null; | ||
| const callbackRef = (el: HTMLImageElement | null) => { | ||
| imgElement = el; | ||
| }; | ||
|
|
||
| const { container } = render( | ||
| <Image | ||
| ref={callbackRef} | ||
| src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png" | ||
| />, | ||
| ); | ||
|
|
||
| // 确保回调 ref 被调用,且指向正确的 img 元素 | ||
| expect(imgElement).not.toBeNull(); | ||
| expect(imgElement).toBe(container.querySelector('.rc-image-img')); | ||
| }); | ||
|
|
||
| // 测试 ref 能够访问 img 元素的属性和方法 | ||
| it('should allow access to img element properties and methods', () => { | ||
| const ref = React.createRef<HTMLImageElement>(); | ||
| render( | ||
| <Image | ||
| ref={ref} | ||
| width={200} | ||
| height={100} | ||
| src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png" | ||
| />, | ||
| ); | ||
|
|
||
| // 确保可以通过 ref 访问 img 元素的属性 | ||
| expect(ref.current?.width).toBe(200); | ||
| expect(ref.current?.height).toBe(100); | ||
|
|
||
| // 可以测试调用 img 元素的方法 | ||
| // 注意:某些方法可能在 jsdom 环境中不可用,根据实际情况调整 | ||
| }); | ||
|
|
||
| // 测试 ref 在组件重新渲染时保持稳定 | ||
| it('should maintain stable ref across re-renders', () => { | ||
| const ref = React.createRef<HTMLImageElement>(); | ||
| const { rerender } = render( | ||
| <Image | ||
| ref={ref} | ||
| src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png" | ||
| />, | ||
| ); | ||
|
|
||
| const initialImgElement = ref.current; | ||
| expect(initialImgElement).not.toBeNull(); | ||
|
|
||
| // 重新渲染组件,但保持 ref 不变 | ||
| rerender( | ||
| <Image | ||
| ref={ref} | ||
| src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png" | ||
| alt="updated alt" | ||
| />, | ||
| ); | ||
|
|
||
| // 确保 ref 引用的还是同一个 img 元素 | ||
| expect(ref.current).toBe(initialImgElement); | ||
| expect(ref.current?.alt).toBe('updated alt'); | ||
| }); | ||
| }); |
Uh oh!
There was an error while loading. Please reload this page.