问题
I am having two issues with using styled components in a hoc wrapper in react.
- The component is rendered, but not with the background color.
- The ComponentWithAddedColors is not valid typescript. Don't know why.
Anyone who can help with this?
interface IProps {
id: string;
left: number;
top: number;
}
export const Node: React.FC<IProps> = ({ id, left, top }) => {
return (
<Container left={left} top={top}>
{id}
</Container>
);
};
function withColors<T>(Component: React.ComponentType<T>) {
const bg = "hotpink";
const ComponentWithAddedColors = styled(Component)`
${bg && `background: ${bg};`}
`;
const result: React.FC<T> = (props) => (
<ComponentWithAddedColors {...props} />
);
return result;
}
const DraggableNode = withColors(Node);
export default DraggableNode;
I have made a code sandbox to illustrate the issue: https://codesandbox.io/s/styled-hoc-xgduo?file=/src/Components/Node/Node.tsx
回答1:
Style Errors Explained
@Mosh Feu's comment pointed me in the right direction.
You can add styles to an already styled component and you can add styles to a custom component, but those two things work differently. You have a chain that goes through both types, so things are getting lost.
When you call withColors(Node)
what this is doing is passing a generated className
prop to Node
. But your custom component Node
never does anything with this prop, so the style is never applied.
The styled method works perfectly on all of your own or any third-party component, as long as they attach the passed className prop to a DOM element.
Style Errors Fixed
If we edit Node
to use this className
, we get the color!
export const Node: React.FC<IProps & {className?: string}> = ({ id, left, top, className}) => {
return (
<Container left={left} top={top} className={className}>
{id}
</Container>
);
};
TS Errors Explained
As far as the typescript errors are concerned, you're getting an error about assigning your props T
to the props of a styled component (ComponentWithAddedColors
), which shows up as a bunch of crazy nonsense:
(props: (Pick<Pick<(PropsWithoutRef & RefAttributes<Component<T, any, any>>) | (PropsWithRef<PropsWithChildren> & {}), Exclude<...> | ... 1 more ... | Exclude<...>> & Partial<...>, Exclude<...> | ... 1 more ... | Exclude<...>> & { ...; } & { ...; }) | (Pick<...> & ... 2 more ... & { ...; })): ReactElement<...>
This is mainly because of ref forwarding through the ForwardRefExoticComponent
type.
But we can work backwards to get the expected props type from the component type using a utility type:
type PropsOf<T> = T extends React.ComponentType<infer P> ? P : never;
So ComponentWithAddedColors
has props PropsOf<typeof ComponentWithAddedColors>
. We could use that, but we also know that ComponentWithAddedColors
has type StyledComponent<React.ComponentType<T>, any, {}, never>
, so we can go back a step further:
type StyledProps<InitialProps> = PropsOf<StyledComponent<React.ComponentType<InitialProps>, any, {}, never>>
So ComponentWithAddedColors
has props StyledProps<T>
.
TS Errors Fixed
That said, all of this is unnecessary, at least in the example you've shown. You are passing all of the props of ComponentWithAddedColors
through to ComponentWithAddedColors
, so result
is the same thing as the component itself. Just return it directly.
function withColors<T>(Component: React.ComponentType<T>) {
const bg = "hotpink";
return styled(Component)`
${bg && `background: ${bg};`}
`;
}
来源:https://stackoverflow.com/questions/64596282/styled-components-in-a-hoc-react-component