Reactのディレクトリ構成でAtomicデザインをやめた話
Atomic デザインをやめた
結論から言うと Atomic デザインを React のディレクトリ構成に当て込むのをやめました。結構ツラミが出てきてしまった感じです。とはいえ、Atomic デザインを批判するわけではなく、むしろ概念というか考え方は好きな部類です。
あくまでデザインシステムであり、ディレクトリ構成に当て込むべきものではないなと。
以前までのディレクトリ構成はこんな感じ
Atomic デザインが話題になった頃からそれに倣ったディレクトリ構成にしていました。Next.js のプロジェクトになってしまいますが大体こんな感じ。
├── common
│ ├── config
│ ├── styles
│ ├── types
│ └── utils
├── components
│ ├── atoms
│ ├── molecules
│ ├── organisms
│ └── templates
└── pages
├── 404.tsx
├── _app.tsx
├── _document.tsx
├── blog
├── contact-thanks.tsx
├── flow.tsx
├── index.tsx
└── works
Atomic デザインの限界
React や Next.js のディレクトリ構成に Atomic デザインの理論を落とし込むことによる問題点というか弊害というか限界が見えてきたなと思っています。個人的には以下の理由で Atomic デザインをやめました。
- 名が体を表していない
- Organisms が増えすぎてしまう病
- pages に依存した Component などがどうしても発生する
この 3 点が主な理由なんですが、一つづつ解説をします。
名が体を表していない
Atomic デザインが見た目の意味になっているので、無理やり React のディレクトリ構成に落とし込んでもしんどいんですよね。Atomic デザインを知らない人からすると organisms
って言われても分からんのですよね。一般的な言葉ではないので。。。
organisms が増えすぎてしまう病
おそらくですが、organisms が増えてしまいがちなのは私の構成だからだと思います。人によっては molecules の場合もあるかなと。
私の場合は organisms
ではデータの取得や state の更新をして良い 紳士協定
にしていました。なので一覧を表示する Table があったとすると UsersTable や HogesTable などが出来すぎてしまっていました。これについては私の Component の作り方が悪かったのも原因の一つではありますし、そもそも Pages でデータの取得や state の更新をすべきというのが Atomic デザインの考え方ではあると思うんですが、props でバケツリレーもしんどいんですよね。
そうなるともう少し小さい粒度でデータの取得や state の更新をしなければならず、organisms
などで似たような Component が乱立するというジレンマに陥っていました。
多分この時点で Atomic デザインの限界だったんですよね笑
pages に依存した Component などがどうしても発生する
上記からの派生なんですが、小さい粒度でデータの取得や state の更新するためにorganisms
コンポーネントを作っていくと結局 pages に依存していくんですよね。例えば
const UsersPage: React.FC = () => {
return (
<>
<PageMeta title="ユーザー一覧" />
<UsersSearchFields />
<UsersTable />
</>
);
};
export default UsersPage;
このような形で organisms
を配置して pages を構成していくイメージですが、ユーザーページでしかこれらのコンポーネントは名前の通りで使わないんですよね。なので users 配下で使うコンポーネントとして下記のようなディレクトリ構成を試したりもしました。もうこうなってくるとディレクトリ構成を Atomic デザインにしていく必要ってないんですよね笑
└── users
├── organisms
│ ├── UsersSearchFields.tsx
│ └── UsersTable.tsx
└── index.tsx
今の所たどり着いたディレクトリ構成
10 月からやっている新しいプロジェクトでは試行錯誤の上こんな感じでやり始めています。
├── common
│ ├── constants
│ │ └── index.tsx
│ ├── hooks
│ │ └── index.tsx
│ ├── styles
│ │ ├── app.css.ts
│ │ ├── globals.css
│ │ └── theme.css.ts
│ ├── types
│ │ └── index.tsx
│ └── utils
│ ├── apiClient.ts
│ └── imageLoader.ts
├── components
│ ├── functional
│ │ └── Authenticator.tsx
│ ├── layouts
│ │ ├── Header
│ │ ├── Header.stories.tsx
│ │ ├── index.tsx
│ │ └── style.css.ts
│ ├── ui-elements
│ │ ├── Button
│ │ ├── Button.stories.tsx
│ │ ├── index.tsx
│ │ └── style.css.ts
│ └── ui-parts
│ ├── FieldCheckbox
│ ├── FieldCheckbox.stories.tsx
│ └── index.tsx
├── features
│ ├── Users
│ ├── api
│ │ └── index.ts
│ ├── components
│ │ └── index.tsx
│ ├── hooks
│ │ └── index.ts
│ ├── helper
│ │ └── index.ts
│ ├── index.ts
│ ├── test
│ │ └── index.test.ts
│ └── types
│ └── index.ts
└── pages
├── 404.tsx
├── _app.tsx
├── _error.tsx
└── index.tsx
先ほども述べた通り概念というか考え方として Atomic デザインは 小さく作って組み合わせる
というものなので、React の思想とも近くとても優れていると思います。まずやったこととして大きいのは
- components 内の仕分けする際の名称の変更
- features の導入
の 2 点です。
components 内の仕分けする際の名称の変更
こちらを参考にさせていただいて、汎用的に使うコンポーネントの名称を ui-elements
とui-parts
、functional
に分けました。
ui-elements
ui-elements は、Atomic デザインでいうところの atoms
に相当するものになります。state も更新などは行うことはしません。
例えば Button コンポーネントであればこのような感じです。
export const Button: FCX<Props> = ({
color,
size,
onClick,
children,
className,
type,
}) => {
return (
<button
className={classnames(
ButtonRecipe({
color,
size,
}),
className
)}
onClick={onClick}
type={type}
>
{children}
</button>
);
};
また、stories や style もコンポーネントと同じ階層に入れるようにしています。
ui-parts
ui-parts は Atomic デザインでいうところの molecules 相当になります。ui-elements
を組み合わせたりしてコンポーネントを成形します。また、useState
などで state の更新・保持をしても良いことにしています。ここについては結構悩んだんですけど、データの取得など API が絡まなければ良いかなと。
functional
これは参考にさせていただいたもので、良いなーっと思ったので即導入!!
見た目を伴わない系のコンポーネントなどを配置するようにしたりしてます。例えばログイン判定などですね。
export const Authenticator: FC = ({ children }) => {
const { isAuthenticated } = useAuth0();
const router = useRouter();
useEffect(() => {
if (!isAuthenticated) {
router.push("/");
}
}, [isAuthenticated, router]);
return <Main>{children}</Main>;
};
layouts
layouts は文字通りレイアウトに関するコンポーネントを配置します。結構実験的に入れてみてる感じなので今後いらなくなるかも。
Header や Footer などを入れています。
features の導入
こちらを参考にさせていただいて、今まで私がやっていた pages 配下に organisms を配置したり、該当ページでしか使わないメソッドや型定義を機能ごとに配置するようにしています。
これが良いなと思った点としては
- 機能ごとなので、ページを跨いで使っても違和感がない
- 機能ごとにテスト書ける
かなと思っています。pages 配下に organisms を配置していた時には結構しんどいというか違和感があってやらなかったんですが、例えばユーザー一覧をこっちのページでも使いたいといった場合、page を跨いで使うので気持ち悪いんですよね。
なので、他ページでもユーザー一覧用コンポーネントを作るか、汎用的に扱う organisms ディレクトリを作ってそこに置くかなどをしていました。ですが features
であればそういった場合でも違和感なく使えて、しかもテストも機能ごとにまとめられる!これを考えた人天才です!
終わりに
現在サービスを絶賛開発中ですが結構気に入っているディレクトリ構成になりました。また、今後は components 配下に providers を作っても良いかなとかも考えていたりしています。規模がもっと大きくなったらどうなるかなというのもあるので、しばらくしたら悩んでいるかもしれません。
こういった構成とか考えるのはやっぱ酒飲みながらできると楽しいんですよね〜。コロナが収束すれば楽しみ増えるなと思いながら記事にしました。
Author And Source
この問題について(Reactのディレクトリ構成でAtomicデザインをやめた話), 我々は、より多くの情報をここで見つけました https://zenn.dev/brachio_takumi/articles/2ab9ef9fbe4159著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Collection and Share based on the CC protocol