Skip to content

Commit

Permalink
feat(utils): add withRef hoc (#1356)
Browse files Browse the repository at this point in the history
Co-authored-by: ZhaoChen <ittisennsinn@gmail.com>
  • Loading branch information
itiiss and itiisennsinn committed Oct 19, 2021
1 parent 6fd51e9 commit 86312e5
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 0 deletions.
15 changes: 15 additions & 0 deletions src/utils/demos/withRef.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Subtitle, ArgsTable } from '@storybook/addon-docs/blocks';
import { Meta, Story, Canvas } from '@storybook/addon-docs';
import { TextWithRef } from './withRef.stories';

<Meta title="MDX/useControlledState" />

# refWrapper

## 基础用法

### 使用了 refWrapper 的 Text 组件

<Canvas>
<TextWithRef />
</Canvas>
42 changes: 42 additions & 0 deletions src/utils/demos/withRef.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import React, { useRef } from 'react';
import WithRef, { CommonPropsWithRef } from '../withRef';

type InternalButton = typeof Button;

interface RefButton extends InternalButton {
SubButton: InternalButton;
}

const InnerButton: React.FC<CommonPropsWithRef<React.HTMLProps<HTMLButtonElement>, HTMLButtonElement>> = (props) => {
const { forwardRef, children } = props;
return (
<button type="button" ref={forwardRef}>
{children}
</button>
);
};
const ButtonItem: React.FC<React.HTMLProps<HTMLButtonElement>> = () => <button type="button">subButton</button>;

const Button = WithRef<HTMLButtonElement, React.HTMLProps<HTMLButtonElement>, React.ReactNode>(InnerButton, {
SubButton: ButtonItem,
});

export const Demo = () => {
const ref = useRef(document.querySelector('button'));

const { SubButton } = Button as unknown as RefButton;

return (
<div>
<Button ref={ref} type="button">
ButtonWithRef
</Button>
<SubButton />
</div>
);
};

export default {
title: 'utils/TextWithRef',
component: Demo,
};
49 changes: 49 additions & 0 deletions src/utils/withRef.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import React, { CSSProperties } from 'react';

export interface RefWrapperProps<T> {
forwardedRef?: React.Ref<T>;
}

type PropsWithChildren<P> = P & { children?: React.ReactNode };

type PropsWithStyle<P> = P & { style?: CSSProperties };

type PropsWithClassname<P> = P & { classname?: string };

type PropsWithForwardRef<T, P> = P & { forwardRef?: React.Ref<T> };

type CommonProps<T, P> = PropsWithForwardRef<T, PropsWithChildren<PropsWithClassname<PropsWithStyle<P>>>>;

type PropsWithRef<P, T> = P & { ref: React.MutableRefObject<T> };

export type CommonPropsWithRef<P, T> = PropsWithRef<CommonProps<T, P>, T>;

interface ComponentInterface<P> {
defaultProps: Partial<P>;
}

function WithRef<T, P, I>(OptComponent: React.FC<CommonProps<T, RefWrapperProps<T>>>, dependencies?: I) {
const RefWrapper: React.FC<CommonProps<T, P>> = (props) => {
const { forwardRef, ...rest } = props;
return <OptComponent {...rest} ref={forwardRef} />;
};

const ForwardRefComponent = React.forwardRef<T, P>((props, ref) => <RefWrapper {...props} forwardedRef={ref} />);

type InternalComponentProps = typeof ForwardRefComponent;

const Component = ForwardRefComponent as ComponentInterface<P> & InternalComponentProps & I & Record<string, any>;
function moveProperty(
deps: typeof dependencies & Record<string, any>,
res: typeof ForwardRefComponent & Record<string, any>
) {
Object.keys(deps).forEach((key: string) => {
res[key] = deps[key];
});
return res;
}

return moveProperty(dependencies as Record<string, any>, Component);
}

export default WithRef;

1 comment on commit 86312e5

@vercel
Copy link

@vercel vercel bot commented on 86312e5 Oct 19, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.