varHarrie / varharrie.github.io

:blue_book: Personal blog site based on github issues.
https://varharrie.github.io
MIT License
3.66k stars 544 forks source link

React withProps #45

Open varHarrie opened 2 years ago

varHarrie commented 2 years ago

实现withProps函数,用于提前注入组件属性。

import { ComponentProps, ComponentType, ForwardRefExoticComponent, forwardRef, memo } from 'react';

type ElementType = keyof JSX.IntrinsicElements | ComponentType;

type GetProps<C extends ElementType, E> =
  | Partial<ComponentProps<C>>
  | ((props: ComponentProps<C> & E) => Partial<ComponentProps<C>>);

export function withProps<C extends ElementType>(
  Component: C,
): <EP = {}>(getProps: GetProps<C, EP>) => ForwardRefExoticComponent<ComponentProps<C> & EP> {
  const Comp = Component as any; // Fixed "Expression produces a union type that is too complex to represent."

  return (getProps) => {
    return memo(
      forwardRef((props: any, ref) => {
        const injectedProps = typeof getProps === 'function' ? getProps(props) : getProps;
        return <Comp ref={ref} {...props} {...injectedProps} />;
      }),
    );
  };
}

示例:

import { CSSProperties } from 'react';

type InputProps = {
  type?: 'text' | 'password' | 'file';
  value?: string;
  style?: CSSProperties;
};

function Input(props: InputProps) {
  return <input {...props} />;
}

const PasswordInput = withProps(Input)({ type: 'password' });
// const PasswordInput = withProps('input')({ type: 'password' });

const CustomInput = withProps(Input)<{ block?: boolean }>((props) => ({
  style: props.block ? { display: 'block' } : undefined,
}));

const password = <PasswordInput value='123456' />

const custom = <CustomInput block value='hello' />