🦋
【React Native】Tailwind Variants みたいなユーティリティを実装してみる
普段はウェブの開発をするのですが、ネイティブアプリを作りたくなったのでReact Native
を触り始めました。ウェブの開発では、コンポーネントのバリアントの定義にtailwind variants
をよく使うのですが、React Native でも同じ感じの実装がしたいなと思ったので実装してみます。(実装雑です。。)
実装
import { StyleProp, TextStyle, ViewStyle } from 'react-native'
type StyleVariant<T> = {
[key: string]: {
[Key: string]: StyleProp<T>
}
}
type Variant<T> = {
[Key in keyof T]: keyof T[Key]
}
// 作成したバリアントからコンポーネントのPropsを抽出する型
export type VariantProps<T extends (...args: any) => any> = Partial<
Parameters<T>['0']
>
// バリアント定義用の関数
export const createVariant = <T>() => {
return <U extends StyleVariant<T>>(variant: U) => {
return (props: Partial<Variant<U>>) => {
const entries = Object.entries(props)
return entries
.map(([key, value]) => variant[key][value])
.reduce((acc, curr) => {
return { ...(acc as any), ...(curr as any) }
}, {} as StyleProp<T>)
}
}
}
// Viewのスタイリング
export const viewVariant = createVariant<ViewStyle>()
// Textのスタイリング
export const textVariant = createVariant<TextStyle>()
WebはCSSのプロパティがタグによらないので、1つの定義で良いのですが、 React Native
はコンポーネントによって適用できるスタイルが違うので分けてあげる必要があります。
tailwind variants
みたいにまとめたかったのですが、うまくできなかったので分ける形で実装しています。
any は「ユーティリティなら使ってもいいかなあ」と思って使ってます。
使ってみる
以下のように使えます。
今回は例としてFlexboxのコンポーネントを定義してみます。
direction
などのプロパティは、createVariant
のジェネリクスに沿ってエディタの推論が効きます。
バリアント定義
import { viewVariant } from '../../../utils/variant'
export const FLEX_VARIANTS = viewVariant({
direction: {
row: {
flexDirection: 'row'
},
column: {
flexDirection: 'column'
}
},
justify: {
center: {
justifyContent: 'center'
},
start: {
justifyContent: 'flex-start'
},
end: {
justifyContent: 'flex-end'
}
},
align: {
center: {
alignItems: 'center'
},
start: {
alignItems: 'flex-start'
},
end: {
alignItems: 'flex-end'
}
},
gap: {
1: {
gap: 4
},
2: {
gap: 8
},
3: {
gap: 12
},
4: {
gap: 16
}
},
wrap: {
wrap: {
flexWrap: 'wrap'
},
nowrap: {
flexWrap: 'nowrap'
}
}
})
Props
import { ViewProps } from 'react-native'
import { VariantProps } from '@/utils/variant' // 任意のパス
import { FLEX_VARIANTS } from './const'
export type FlexProps = ViewProps & VariantProps<typeof FLEX_VARIANTS>
コンポーネント
import { View } from 'react-native'
import { FlexProps } from './type'
import { FLEX_VARIANTS } from './const'
export const Flex = ({
children,
style,
direction,
justify,
align,
gap,
wrap,
...props
}: FlexProps) => {
return (
<View
{...props}
style={[
style,
{display: "flex"},
FLEX_VARIANTS({ direction, justify, align, gap, wrap })
]}
>
{children}
</View>
)
}
おわり
雑ですが、tailwind variants
に似た感じでスタイルを定義できるようになりました。
React Native初心者なので、検討できてない点があればコメントいただけると嬉しいです🙏
Discussion