Skip to content
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

fix: add React.forwardRef to fix warning #45390

Merged
merged 7 commits into from
Oct 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
28 changes: 17 additions & 11 deletions components/float-button/BackTop.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
import React, { useContext, useEffect, useState } from 'react';
import VerticalAlignTopOutlined from '@ant-design/icons/VerticalAlignTopOutlined';
import classNames from 'classnames';
import CSSMotion from 'rc-motion';
import React, { memo, useContext, useEffect, useRef, useState } from 'react';
import FloatButton, { floatButtonPrefixCls } from './FloatButton';
import type { ConfigConsumerProps } from '../config-provider';
import { ConfigContext } from '../config-provider';
import { composeRef } from 'rc-util/lib/ref';

import getScroll from '../_util/getScroll';
import scrollTo from '../_util/scrollTo';
import throttleByAnimationFrame from '../_util/throttleByAnimationFrame';
import type { ConfigConsumerProps } from '../config-provider';
import { ConfigContext } from '../config-provider';
import FloatButtonGroupContext from './context';
import type { BackTopProps, FloatButtonProps, FloatButtonShape } from './interface';
import FloatButton, { floatButtonPrefixCls } from './FloatButton';
import type { BackTopProps, FloatButtonProps, FloatButtonRef, FloatButtonShape } from './interface';
import useStyle from './style';

const BackTop: React.FC<BackTopProps> = (props) => {
const BackTop = React.forwardRef<FloatButtonRef['nativeElement'], BackTopProps>((props, ref) => {
const {
prefixCls: customizePrefixCls,
className,
Expand All @@ -28,10 +30,14 @@ const BackTop: React.FC<BackTopProps> = (props) => {

const [visible, setVisible] = useState<boolean>(visibilityHeight === 0);

const ref = useRef<HTMLAnchorElement | HTMLButtonElement>(null);
const internalRef = React.useRef<FloatButtonRef['nativeElement']>(null);

const mergedRef = composeRef<FloatButtonRef['nativeElement']>(ref, internalRef);

const getDefaultTarget = (): HTMLElement | Document | Window =>
ref.current && ref.current.ownerDocument ? ref.current.ownerDocument : window;
internalRef.current && internalRef.current.ownerDocument
? internalRef.current.ownerDocument
: window;

const handleScroll = throttleByAnimationFrame(
(e: React.UIEvent<HTMLElement, UIEvent> | { target: any }) => {
Expand Down Expand Up @@ -72,18 +78,18 @@ const BackTop: React.FC<BackTopProps> = (props) => {
<CSSMotion visible={visible} motionName={`${rootPrefixCls}-fade`}>
{({ className: motionClassName }) => (
<FloatButton
ref={ref}
ref={mergedRef}
{...contentProps}
onClick={scrollToTop}
className={classNames(className, motionClassName)}
/>
)}
</CSSMotion>,
);
};
});

if (process.env.NODE_ENV !== 'production') {
BackTop.displayName = 'BackTop';
}

export default memo(BackTop);
export default BackTop;
28 changes: 8 additions & 20 deletions components/float-button/FloatButton.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useContext, useMemo } from 'react';
import React, { forwardRef, useContext, useMemo } from 'react';
import classNames from 'classnames';
import omit from 'rc-util/lib/omit';

Expand All @@ -14,16 +14,14 @@ import type {
FloatButtonBadgeProps,
FloatButtonContentProps,
FloatButtonProps,
FloatButtonRef,
FloatButtonShape,
} from './interface';
import useStyle from './style';

export const floatButtonPrefixCls = 'float-btn';

const FloatButton: React.ForwardRefRenderFunction<
HTMLAnchorElement | HTMLButtonElement,
FloatButtonProps
> = (props, ref) => {
const FloatButton = forwardRef<FloatButtonRef['nativeElement'], FloatButtonProps>((props, ref) => {
const {
prefixCls: customizePrefixCls,
className,
Expand Down Expand Up @@ -96,29 +94,19 @@ const FloatButton: React.ForwardRefRenderFunction<

return wrapSSR(
props.href ? (
<a ref={ref as React.RefObject<HTMLAnchorElement>} {...restProps} className={classString}>
<a ref={ref} {...restProps} className={classString}>
{buttonNode}
</a>
) : (
<button
ref={ref as React.RefObject<HTMLButtonElement>}
{...restProps}
className={classString}
type="button"
>
<button ref={ref} {...restProps} className={classString} type="button">
{buttonNode}
</button>
),
);
};

const ForwardFloatButton = React.forwardRef<
HTMLAnchorElement | HTMLButtonElement,
FloatButtonProps
>(FloatButton) as CompoundedComponent;
}) as CompoundedComponent;

if (process.env.NODE_ENV !== 'production') {
ForwardFloatButton.displayName = 'FloatButton';
FloatButton.displayName = 'FloatButton';
}

export default ForwardFloatButton;
export default FloatButton;
11 changes: 6 additions & 5 deletions components/float-button/FloatButtonGroup.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { memo, useCallback, useContext, useEffect, useMemo, useRef } from 'react';
import React, { memo, useCallback, useContext, useEffect } from 'react';
import CloseOutlined from '@ant-design/icons/CloseOutlined';
import FileTextOutlined from '@ant-design/icons/FileTextOutlined';
import classNames from 'classnames';
Expand All @@ -10,7 +10,7 @@ import type { ConfigConsumerProps } from '../config-provider';
import { ConfigContext } from '../config-provider';
import { FloatButtonGroupProvider } from './context';
import FloatButton, { floatButtonPrefixCls } from './FloatButton';
import type { FloatButtonGroupProps } from './interface';
import type { FloatButtonGroupProps, FloatButtonRef } from './interface';
import useStyle from './style';

const FloatButtonGroup: React.FC<FloatButtonGroupProps> = (props) => {
Expand Down Expand Up @@ -45,10 +45,11 @@ const FloatButtonGroup: React.FC<FloatButtonGroupProps> = (props) => {

const [open, setOpen] = useMergedState(false, { value: customOpen });

const floatButtonGroupRef = useRef<HTMLDivElement>(null);
const floatButtonRef = useRef<HTMLButtonElement | HTMLAnchorElement>(null);
const floatButtonGroupRef = React.useRef<HTMLDivElement>(null);

const hoverAction = useMemo<React.DOMAttributes<HTMLDivElement>>(() => {
const floatButtonRef = React.useRef<FloatButtonRef['nativeElement']>(null);

const hoverAction = React.useMemo<React.DOMAttributes<HTMLDivElement>>(() => {
const hoverTypeAction = {
onMouseEnter() {
setOpen(true);
Expand Down
9 changes: 9 additions & 0 deletions components/float-button/__tests__/back-top.test.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import React from 'react';

import FloatButton from '..';
import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
import { fireEvent, render, waitFakeTimer } from '../../../tests/utils';

const { BackTop } = FloatButton;

describe('BackTop', () => {
beforeEach(() => {
jest.useFakeTimers();
Expand Down Expand Up @@ -51,4 +53,11 @@ describe('BackTop', () => {
const { container } = render(<BackTop style={{ color: 'red' }} visibilityHeight={0} />);
expect(container.querySelector<HTMLButtonElement>('.ant-float-btn')?.style.color).toBe('red');
});

it('no error when BackTop work', () => {
const errSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
render(<BackTop visibilityHeight={0} />);
expect(errSpy).not.toHaveBeenCalled();
errSpy.mockRestore();
});
});
7 changes: 6 additions & 1 deletion components/float-button/interface.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import type React from 'react';

import type { BadgeProps } from '../badge';
import type { TooltipProps } from '../tooltip';
import type BackTop from './BackTop';
import type Group from './FloatButtonGroup';
import type PurePanel from './PurePanel';

export interface FloatButtonRef {
nativeElement: HTMLAnchorElement & HTMLButtonElement;
}

export type FloatButtonType = 'default' | 'primary';

export type FloatButtonShape = 'circle' | 'square';
Expand Down Expand Up @@ -63,7 +68,7 @@ export interface BackTopProps extends Omit<FloatButtonProps, 'target'> {
}

export type CompoundedComponent = React.ForwardRefExoticComponent<
FloatButtonProps & React.RefAttributes<HTMLAnchorElement | HTMLButtonElement>
FloatButtonProps & React.RefAttributes<FloatButtonRef['nativeElement']>
> & {
Group: typeof Group;
BackTop: typeof BackTop;
Expand Down
6 changes: 5 additions & 1 deletion components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,11 @@ export type { EmptyProps } from './empty';
export { default as Flex } from './flex';
export type { FlexProps } from './flex/interface';
export { default as FloatButton } from './float-button';
export type { FloatButtonGroupProps, FloatButtonProps } from './float-button/interface';
export type {
FloatButtonGroupProps,
FloatButtonProps,
FloatButtonRef,
} from './float-button/interface';
export { default as Form } from './form';
export type {
FormInstance,
Expand Down