Last updated at
info
この記事は最終更新から1年以上経っています。情報が古くなっている可能性があります。
普段プライベート/仕事共に React を書くことが多いのですが、その中で得た知見や経験則といったものを、他の人のコードレビュー時や設計時に上手く伝えられないケースが多々ありました。また、他の人がどういった書き方をしているか、ということを知るのは引き出しの幅を広げたり意外な発見ができたりします。 そのため、これまでの自分の経験を元にした自己流ベストプラクティスを共有したいと思います。
なお、メモ的な役割があったり、感覚的な部分もあるのであまりまとまった文章になっていないです。
原則全てのコンポーネントがclassName
とstyle
を受け取り、最も外側にある要素に受け渡すようにします。これにより、スタイルの拡張が簡単に行えます。
// css-modules
return <Foo className={styles.centered} />
// styled-components
const CenteredFoo = styled(Foo)`
margin: auto;
`
// emotion(css)
const centered = css`
margin: auto;
`
return <Foo css={centered} />
また、このパターンを使っているとコンポーネント内でどこまでスタイリングをするか
が自然と綺麗に行われるようになり、コンポーネントの抽象化を上手く行えるようになります。
実装する際はこれらだけの Props を作り、Intersection してやる(?)といいです。
import { CSSProperties } from 'react'
interface StyleProps {
className?: string
style?: CSSProperties
}
// /components/Foo.ts
import { FC } from 'react'
interface Prop {
bar: string
}
export const Foo: FC<Props & StyleProps> = ({ bar, ...rest }) => (
<button {...rest}>{bar}</button>
)
ちなみに、プロジェクト毎に何度も書くのは面倒なので、@react-comfy/propsというパッケージで公開しています。
React.Fragment
children
を使う特に React やコンポーネント設計に慣れていない人がやりがちなのが、なんでもかんでもPropsに入れてしまう
というもの。children
をしっかりと使うとコンポーネントツリーが綺麗になるだけでなく以下のようなメリットがあります。
useCallback
等)しやすくなる余談ですが、このやり方は Web Components でコンポーネントを作成する際には必須となります。
// 一覧表示の例
// 微妙
const PostsPage: FC<{ posts: IPost[] }> = ({ posts }) => <Posts posts={posts} />
// いい感じ
const PostsPage: FC<{ posts: IPost[] }> = ({ posts }) => (
<Posts>
{posts.map(post => (
<Post name={post.name} />
))}
</Posts>
)
感覚としては"スタイルを司るコンポーネント"と"動きを司るコンポーネント"に分離する、という感じです。Container/Presentational Component パターンに近いです。
useMemo
する場合これは styled-components や emotion を使っている場合のみですが、コンポーネントの状態に応じてスタイルを切り替える場合に HTML の属性を使う、というものです。
styled-components や emotion では Props に応じて中のスタイルを変えることができますが、変更の都度計算を行うためパフォーマンスに悪影響があります。そのため、HTML の属性やアクセシビリティ系のプロパティを変更して、スタイル側ではそれを参照するようにすると無駄なパフォーマンスコストが発生しなくなります。
style
プロパティを使うのもいいですが、useMemo
を使わないと参照が毎回変わる、詳細度が高くなる、座標のような超動的なものと一緒になってしまう、等の理由からこちらのやり方を推奨します。
const Dialog = ({ children, visible }) => (
<Container aria-hidden={!visible}>
{children}
</Contaienr>
)
const Container = styled.div`
display: none;
&[aria-hidden="false"] {
display: block;
}
`
特にアクセシビリティ系(主にaria-*
)のものを使うと、自然とアクセシビリティも高くなり※1一石二鳥なので非常におすすめです。
※1 ... どんなプロパティがあってどう使うかの知識がある、もしくは学びながら進める前提です。知らずに適当に使えばアクセシビリティはめちゃくちゃ下がります。
プロジェクトに Storybook を導入し、Container/Presentational Component パターンでコンポーネントを作成することで、コンポーネントとロジックの作成作業が完全に分離されます。複数人で作業をする場合はもちろん、個人で作業する場合でも非常に効果があります。
React やコンポーネント作成に限らないですが、しっかりと方針やルール、ガイドラインを設けてやるのが大切です。
もし、これ違くない?
とか、こうやったらもっとシンプルになるよ
とかあればIssueでもPRでももらえると嬉しいです!