Search code examples
reactjstypescriptstyled-components

Styled component in HOC


I want to add styles to my component wrapper using higher order component. Typescript says there is error with ComponentWithAdddedColors.

type Props = {
  bg?: string;
};

function withColors<TProps>(
  Component: React.ComponentType<TProps>
): React.ComponentType<TProps & Props> {

  const ColoredComponent: React.ComponentType<TProps & Props> = props => {
    const { bg, ...componentProps } = props;

    const ComponentWithAdddedColors = styled(Component)`
      ${bg && `background: ${bg};`}
    `;

    return <ComponentWithAdddedColors {...componentProps} />; //Typecheck error
  };

  return ColoredComponent;
}

When I want to return Component that was passed to HOC with {...componentProps} there is also typecheck error.

...
{
  const ColoredComponent: React.ComponentType<TProps & Props> = props => {
    const { bg, ...componentProps } = props;

    return <Component {...componentProps} />; //Typecheck error
  };

  return ColoredComponent;
}

But, when I pass everything to Component with {...props} there is not typecheck error.

...
{
  const ColoredComponent: React.ComponentType<TProps & Props> = props => {
    return <Component {...props} />; //No error
  };

  return ColoredComponent;
}

Solution

  • Is this what you're trying to do?

    export function withColors<T>(Component: React.ComponentType<T>) {
        return styled(Component)<Props>`
            ${({ bg }) => bg && `background: ${bg};`}
        `
    }
    
    const Foo: React.FC<{ bar: string }> = props => <div>{props.bar}</div>
    const ColoredFoo = withColors(Foo)
    export const redFoo = <ColoredFoo bg="red" bar="baz" />
    

    If you wanted to lock-in your colors and not expose the color props, however, then I'm afraid you might have exposed a TypeScript bug. I can't seem to get around it myself (without using additionalProps as any); however, I did approach it a bit differently.

    function withColors<T>(Component: React.ComponentType<T>, additionalProps: Props) {
        const { bg } = additionalProps;
        const ComponentWithAddedColors = styled(Component)<Props>`
            ${bg && `background: ${bg};`}
        `
        const result: React.FC<T> = props => (
            <ComponentWithAddedColors {...props} {...(additionalProps as any)} />
        )
        return result
    }
    
    export const RedFoo = withColors(Foo, { bg: 'red' })