Обложка канала

Paqmind - про Frontend / Web / React

444 @Paqmind

Канал о программировании и веб-разработке. Мы создаем уникальные туториалы, гайды, составляем дорожные карты для самообучения. Основные темы: Frontend, React, Next.js.

Paqmind - про Frontend / Web / React

5 лет назад
Открыть в
How to use `React.forwardRef` & `React.memo` in TypeScript with Namespace pattern For example you have the following working code:
export function Flex(props : FlexProps) : JSX.Element { ... }

function FlexRow(props : FlexRowProps) : JSX.Element { ... }

Flex.Row = FlexRow // !!! works 
But it immediately breaks if you add memo or forwardRef:
export const Flex = React.forwardRef(function Flex(props : FlexProps, ref : any)) : JSX.Element { ... })

function FlexRow(props : FlexRowProps) : JSX.Element {}

Flex.Row = FlexRow // !!! Error: property `Row` does not exist on ...
React developers have been struggling with this problem for years. And the proposed solutions are insanely verbose:
export interface Props = {
   ...yourPropsHere;
};

export interface CompoundedComponent extends React.ForwardRefExoticComponent<Props & React.RefAttributes<HTMLInputElement>> {
   yourStaticFunctionOrSomethingLikeThat: () => void;
}

const Component = React.forwardRef<HTMLInputElement, Props>((props, ref) => (
    <input ref={ref} {...props} />
)) as CompoundedComponent;

Component.yourStaticFunctionOrSomethingLikeThat = () => {};
Total mess:
type WithStatics<
  C extends React.ComponentType<any>,
  StaticCmps extends {
    [attachedComponents: string]: React.ComponentType<any>;
  } = {}
> = C & StaticCmps;

type GridProps = {};
type CellProps = {};

const Cell: React.FunctionComponent<CellProps> = props => <div {...props} />;

const Grid: WithStatics<
  React.FunctionComponent<GridProps>,
  { Cell: typeof Cell }
> = props => <div {...props} />;

Grid.defaultProps = { color: "red" };
Grid.Cell = Cell;
Recently, I have found a much easier way to achieve the same result:
tsx
const Flex = React.forwardRef(function Flex(...) {
  ...
})

const FlexRow = React.forwardRef(...)
const FlexColumn = React.forwardRef(...)

const FlexNamespace = Object.assign(Flex, {Row: FlexRow, Column: FlexColumn})

export {FlexNamespace as Flex}
Now you can use Flex, Flex.Row, and Flex.Column with TS being happy. The magic line is Object.assign which does not cause the same type issue as Flex.Row = FlexRow does.