実践的なReactプロジェクトを作成するまで(1)

React+Reduxアプリのチュートリアルを終えて本格的な開発に入るぐらいのタイミングで、ディレクトリ構成について悩み、他のプロジェクトを参考にしようと思って色々調べてみたのですが、これといってベストプラクティスは定まっていないように思えました。

当然プロジェクトの内容次第で変わってくる部分ではあるのですが、一般的なケースにおける知見として、開発が進んでいくにつれ、なんとなくこうした方が良いのではないか、というポイントが段々と見えてきたので共有します。

component

component群については、componentcontainerの2種類に大きく分け、reduxのconnectをしたcomponentcontainerと定義するのが主流と思われます。

containerと呼ぶのにcomponentの大小や複雑さは関係なく、小さいパーツであってもconnectが必要なケースであればconnectして良く、containerと呼ぶこととします。

そのようにすることで、親componentの変更をトリガーにすべての子componentでrenderが走ることを防げるため、パフォーマンスも向上します。モバイル版Twitterにおける個別ツイートのdivなども、それぞれが個別にconnectされた設計になっているようです。

また、ページ単位のcontainerをpagesという名前にしているプロジェクトもありますが、SPAの考え方からして、componentとページとを1対1の関係で結びつけてしまうのは筋が悪そうです。

いわゆるatomic designを根拠にしているかもしれないのですが、たとえばcomponentの種類をatom, moleculeと細かく分類する事についてもあまり意味がない(かつ、明確な分類が難しい)という感じがしており、少なくとも最初のうちは厳格に分類ルールを定めないほうがよさそうです。

Store設計、分割

Store設計についても、ページ単位ではなくドメイン単位ですべき、という実感があります。以下の記事で素晴らしい考察とまとめがされており、特に付け加えることは無いのですが、

flux/ReduxのStoreの持ち方について思う事

これはディレクトリの構成にも少なからず影響を与えることになると思います。巷でよく見るのは以下に近いような構成なのですが、

src
|_ components
|_ containers
|_ constants
|_ reducers
|_ auth.js
|_ counter.js
|_ actions
|_ auth.js
|_ counter.js

このようにmodule単位でまとめた構成のほうが合理的な気がしていて、推奨しているブロガーも多いです。

src
|_ components
|_ containers
|_ modules
|_ auth.js
|_ counter.js

module(ドメイン)単位でファイルを置き、action, action creator, reducer, constantなどを1つのファイルにまとめることで、探しやすさや、import文における相対参照のすっきり具合がかなり違ってきます。

結果として、1ファイルの行数が増えることにはなりますが、actionreducerは極力pureな関数として書かれるべきものであるため、基本的にはsetter的な関数の羅列となり、可読性はそこまで損なわれません。

action, action creator, reducer

こちらのducksというproposalに従うのが吉です。

erikras/ducks-modular-redux

actionaction creatorを毎回書くのがだるいという場合は、redux-actionsreduxsauseといったライブラリを使って、redux周りのコード量を減らすこともできます。

redux-actions — Flux Standard Action utilities for Redux.
reduxsauce — Some aesthetic toppings for your Redux meal.

import文

また、import文での記述を簡素にするための方法として、以下のように配下のcomponentのexportだけを行うindex.jsをフォルダ内に置き、

component/index.js

export Auth from './Auth/Auth'
export Counter from './Counter/Counter'
export Todo from './Todo/Todo'

物理的に遠いファイルをimportする際のエンドポイントとするやり方があります。
importする側はこういう感じです。componentのimportを一行にまとめることができます。

container/SomeContainer.js

import {Auth, Counter, Todo} from '../component'

ファイルを新しく作るたび、component/index.jsに忘れず追加しないといけない手間はありますが、import文を書き換える頻度はそこそこ高いので、ファイルの数やフォルダ階層が複雑になってきたら検討する価値はあると思います。

また、このように、関連性の高いcomponentをフォルダでまとめる方法もありますが、

src
|_ components
|_ Counter
|_ index.js
|_ CounterImage.js
|_ CounterText.js

フォルダ内に1つしかファイルが無い場合、index.jsに実際のコードを書くことになり、ファイル名でソースコードの区別が付きづらくなってしまい、テキストエディタなどのファイル検索でindex.jsが大量に出てくるわでどれがどれかよくわからんというつらみが生まれます。

階層は浅めにして、多少数が多くてもファイルはフラットに並べ、再利用性が低いが関連性の高いコンポーネント同士はなるべく1つのファイルにまとめて定義することでファイル数を削減する、編集するファイルを切り替える際はエディタのインクリメンタルサーチを活用する、などとするのが吉な気がする今日この頃です。

以上です。うちではここはこうしている、こうした方が良さそう、などのアイデアがあればいただけると嬉しいです。