Tweet
Logo
    React の Component 設計と Atomic Design
    React の Component 設計と Atomic Design

    React の Component 設計と Atomic Design

    • はじめに
    • Atomic Design (アトミックデザイン) とは
    • 実装における課題感
    • 1. 同レイヤー同士の import を許すか論争が勃発する
    • 2. レイヤーが足りなくなる
    • 課題を加味した上での設計
    • 実装のイメージ
    • app.js
    • pages.js
    • さいごに

    はじめに

    DROBE では React のコンポーネント設計に Atomic Design を取り入れていますが、特にデザインにおける設計と React における実装という目的の違いから、実装していると微妙に概念が取り入れ辛く、しばしば方針に迷う事があります

    この記事では現在 DROBE でどのように Atomic Design と実装の整合性に折り合いをつけているのかを解説します

    Atomic Design (アトミックデザイン) とは

    Atomic Design はアメリカの Web デザイナーである Brad Frost 氏が提唱したデザインシステムの設計方法です

    Atomic Design by Brad Frost

    We're tasked with making interfaces for more users in more contexts using more browsers on more devices with more screen sizes and more capabilities than ever before. That's a daunting task indeed. Thankfully, design systems are here to help.

    atomicdesign.bradfrost.com

    Atomic Design by Brad Frost

    それぞれのレイヤーの詳細な説明はここでは省略しますが Atomic Design では UI を 5 つのレイヤーに分解してデザインする事を提唱しています

    https://atomicdesign.bradfrost.com/chapter-2/

    5 つのレイヤーとは Atoms, Molecules, Organisms, Templates, Pages になります

    実装における課題感

    Atomic Design の思想をそのまま React の Component 設計に当てはめようとすると、様々な所で課題が出てきます。もちろん思想や運用によっては課題とならない事も多々あるとは思いますが、DROBE で実際に発生した課題は以下のようなものになります

    1. 同レイヤー同士の import を許すか論争が勃発する

    同じレイヤー間の import を許すべきか、という論争が勃発します

    例えば Organisms は Organisms を import して良いのか、Molecules は Molecules を import して良いのか、という議論です

    image

    ちなみに私は同一レイヤー同士の import は許したくない派です (笑)

    2. レイヤーが足りなくなる

    再利用可能な Component という形で考えると、レイヤーが 5 つしか無いと往々にしてレイヤーが足りなくなります

    例えば、ローディング中にクルクルが出るボタン、などを考えると、LoadingButton というものを Atoms として定義しがちですが、同様にクルクルが出る form input を LoadingInput というような形にしていくと、結局クルクルの部分は全ての Component に実装するハメになります

    ここで同一レイヤー同士の import を許容すると Loading を Atoms として定義しつつ LoadingButton も Loadingainput も Loading に依存する、という形になりますが、同一レイヤーの import を許容しない場合はすぐにレイヤーが足りなくなるか、結局同じような実装を複数の Component に書く必要が出てきます

    課題を加味した上での設計

    上記のような課題を踏まえて DROBE では以下のようなルールと共に Atomic Design にレイヤーを 2 つ追加しています (Atoms の下に Quantum (量子) を、Molecule と Organisms の間に Compounds (化合物) を追加)

    image
    1. 一番上に app.js があって、そこで react router の定義をする
    2. 同じレイヤーの component は import してはならない
      • これをしなければいけない場合は component の設計を見直すべき
    3. Query (API) を呼んで良いレイヤーは Organism 以上とする
    4. React hooks を使って良いレイヤーは Compounds 以上とする
      • 逆の言い方をするとMolecules 以下は state を持つべきではない
    5. Propos として受け取って良い型を定義する
      • Atoms は string, boolean, numeric のみ
      • Molecules は array も受け取れるが、その中身は string, boolean, numeric に限定する
      • Compounds 以上のレイヤーはどんな型でも受け取れる

    まとめると、それぞれのレイヤーの役割と制約条件は以下のようになります

    レイヤーの役割

    Layer 名
    目的
    相互参照
    Query を呼んで良いか
    hooks
    props で受け取って良い型
    Pages

    URL に対応させる React router の path が各 page にマッピングされる

    無し

    ○

    ○

    Templates

    header / menu などを定義 自身で Organisms などを実体化する事はしない

    無し

    ○

    ○

    stringbooleanarray<any>numericany
    Organisms

    Hooks を呼べる

    無し

    ○

    ○

    stringbooleanarray<any>numericany
    Compounds

    Molecule を複数使った構造だが hooks は呼ばない 任意の型を props として受け取れる

    無し

    X

    userState useCallback useEffect

    stringbooleanarray<any>numericany
    Molecules

    Atoms を複数持てる構造 props としては js の組み込み型のみを受け取る

    無し

    X

    X

    stringbooleannumericarray<string>array<numeric>
    Atoms

    html の生タグ (div, p img など) と react material ui のタグのみで構成される最小単位(quontum 除く)

    無し

    X

    X

    stringbooleannumeric
    Quantums

    これ以上分解できない最小単位特殊型ではあり、現在はアイコンのみ

    無し

    X

    X

    実装のイメージ

    全部で 7 レイヤーとなった際の react の app.js と Pages レイヤーの実装イメージは以下のようになります

    app.js

    <Route
      path={path}
      render={({ match }) => (
    	  <Page path={path} />
      )}
    />

    app.js は基本的には routing を行う役割に限定されます (もちろん redux などの data store はここで設定しますが)

    React router の path と Pages レイヤーのマッピングを行います

    pages.js

    Pages レイヤーの責務は以下になります

    • どの Templates を使うかを選択する
    • Templates の中身として何を使うか
      • 端的に言うと Organisms を実体化するのは Pages の責務になります
    // render 
    const renderContent = () => {
    	return (
        <Organism props={"dummy"}/>
      )
    }
    
    
    return (
      <AppTemplate path={path}>
        { renderContent() }
      </AppTemplate>
    )
    

    さいごに

    DROBE における Atomi Design を活かした設計を紹介しました

    実際に作っていると、何を Atoms にして何を Molecules にするかなどといった議論をレビューなどを通して行ない、チーム内のコンセンサスを作っていくという事が必須になりますが、それは Component といった階層構造を伴うフロントエンドの設計や実装において必要なプロセスであると考えています

    DROBE でも今回ご紹介した Component 設計に至るまでに前身となる 2 つの React Project がありました (現在でも古い設計のままメンテされています)

    Frontend は技術の移り変わりが激しいので、新しく React Project を作るたびに喧々諤々議論しながら設計を固めていくという所に面白みを感じています

    © 2025 DROBE All rights reserved.