So I have been playing around with type systems in JavaScript and for the most part things are working however there is an issue with styled-components. I can't seem to find a good way to apply flow to the props of a styled-component. So far the only solution I see is:
export type ButtonPropTypes = ReactPropTypes & {
styleType: 'safe' | 'info' | 'warning' | 'danger' | 'link',
isPill: boolean,
isThin: boolean,
};
export const ButtonStyled = styled.button`
${generateBaseStyles}
${hoverStyles}
${fillStyles}
${thinStyles}
${linkStyles}
`;
export const Button = (props: ButtonPropTypes) => <ButtonStyled {...props} />;
It seems pretty excessive that I have to create 2 component for every styled component.
I am hoping my google skills are just crap and I am missing something but is there a better way to do this other than multiple components per styled component?
Because what happens is when the component's Props changes, then the component will re-render and the style will regenerate. Therefore it makes sense to keep it separate. So if you read further on to the Adapting based on props section, they explain this:
In today's article, we'll talk a little bit about props. Props is a React special keyword that stands for properties and it is used to pass data between components. The styled-component lets you use props and change the CSS with it, as well as do other things that are out of the scope for today.
Properties or props for short, are used to make UI components more dynamic, consider you need two buttons with a different border-color but other CSS properties are the same, you can pass props to each button, with some set of conditions that if that props value is available it should change the CSS property. let’s do this!
The styled-component lets you use props and change the CSS with it, as well as do other things that are out of the scope for today. For instance, if props are "primary" change these colours, so you can dynamically change your button based on anything that you want.
Yes! There is a better way. The trick is to declare the type of the component created by styled-components. You can do this by casting the result returned by styled.button`...`
to the type of a React component that takes in your desired props. You can generate the type of a React component that takes in arbitrary props with type mytype = React.ComponentType<MyProps>
.
// @flow
import styled from 'styled-components'
// Make sure you import with * to import the types too
import * as React from 'react'
// Mock function to use styleType
const makeStyles = ({styleType}) => ''
export type ButtonPropTypes = {
styleType: 'safe' | 'info' | 'warning' | 'danger' | 'link',
isPill: boolean,
isThin: boolean,
};
export const ButtonStyled = (styled.button`
${makeStyles}
${({isPill}) => isPill ? 'display: block;' : ''}
${({isThin}) => isThin ? 'height: 10px;' : 'height: 100px;'}
`: React.ComponentType<ButtonPropTypes>) // Here's the cast
const CorrectUsage = <ButtonStyled styleType="safe" isPill isThin/>
const CausesError = <ButtonStyled styleType="oops" isPill isThin/> // error
const CausesError2 = <ButtonStyled styleType="safe" isPill="abc" isThin={123}/> // error
I've hosted the code on GitHub for local reproduction (since Flow's sandbox doesn't work with external dependencies): https://github.com/jameskraus/flow-example-of-styled-components-props
In addition to James Kraus' answer, if you're using flow-typed
(and have installed the package for your version of styled-components
) you can essentially:
import styled, {type ReactComponentStyled} from 'styled-components'
type Props = {
color?: string
}
const Button: ReactComponentStyled<Props> = styled.button`
color: ${({color}) => color || 'hotpink'};
`
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With