Help us understand the problem. What is going on with this article?

【Atomic Design入門】Todoアプリを作りながらAtomic Designを理解する

はじめに

こんにちは、masa08です。普段はエンジニア限定シェアハウス「GAOGAO-TOKYO」で過ごしつつ、スタートアップで業務委託エンジニアをしつつ、大学生してます(早く卒業したい)。

今年は多くの新しい技術に触れた年でした。特にフロントエンドに関しては知見を得たことが多かったです。去年の今ごろはどうにかしてJavaScriptがかけるくらいの実力だったのですが、今年一年通じて、TypeScriptやReact、Atomic Designと様々な技術や考え方に出会い、実務を通じて自分の理解を深めることができました。
今回の記事ではアウトプットも兼ねて、Atomic Designに沿ったディレクトリ構成でReact×TypeScriptを用いてTodoアプリを作っていきたいと思います。自分自身わからないことも多いので、何かあればご指摘いただけると幸いです。

スクリーンショット 2019-12-22 1.23.35.png

対象者

  • JavaScriptの基本的な知識を持っている
  • Reactの基本的な知識を持っている
  • Atomicデザインに触れたいと考えている
  • TypeScriptに触れたいと考えている

この記事を通じて得られること

  • Reactでアプリケーションを作る方法
  • TypeScriptでコードを書く経験
  • Atomic Designに関する知識

Atomic Designとは

スクリーンショット 2019-12-21 20.37.55.png
画像引用元サイト: Atomic Design Methodology

Atomic Designとは、Webサイト上に存在するものすべてをコンポーネントとみなし、粒度ごとにコンポーネントの種類を分けて管理していく、コンポーネントベースの開発手法のことです。小さいコンポーネントを組み合わせて、より大きなコンポーネントを作っていきます。以下の5つの粒度に分けて、コンポーネントを大別します。この際、上位層は下位層に依存します。

  • Atoms(原子)
  • Molecules(分子)
  • Organisms(有機体)
  • Templates(テンプレート)
  • Pages(ページ)

Atoms

Atoms層は、それ以上UIとして分解できない最小要素のことです。テキストやボタンが代表的な要素です。

Molecules

Molecules層は、2つ以上ののAtomsが組み合わされて作られたコンポーネントです。検索フォームなどが代表的な要素です。

Organisms

Organisms層はMoleculesやAtomsで構成されるコンポーネント群です。Headerなどが代表的な要素です。

Templates/Pages

Template層はページの雛形、Pages層は実際のページ、すなわちTemplate層にコンテンツを流し込んだものになります。ここにきてユーザーから見えるページが完成します。organisms層を中心として、Templates/Pagesを組み立てていきます。

Atomic Designを採用するメリット

従来の開発手法と比べて、コンポーネントベースでのUI開発には以下の利点があります。

  • アプリケーションのメンテナンスしやすくなる
  • 解決する問題が小さくなる(責務の分離)
  • チーム内で共通認識を持つことができる
  • コンポーネントの再利用、平行開発等にによって、開発のスピードが速くなる

実際にコードを書いて、Atomic Designの雰囲気を感じてみましょう。

参考サイト
Atomic Design について調べて見た
Atomic Designを分かったつもりになる
React, Components, and Design

Todoアプリを作る

Atomic Designがどのようなものか、atomsとmolecules、organisms、pagesを作る過程を追いながら確認していきます。

環境を整える

まず最初にnodeをインストールしましょう。以下のサイトからダウンロードします。
https://nodejs.org/ja/download/
ダウンロードしたファイルを開いて、nodeをインストールします。インストールが終わったら、ターミナルで以下のコマンドを打って、nodeがインストールされているのかを確認しましょう。またnodeをインストールするとnpmを一緒にインストールされるので、そちらも確認しましょう。

$ node -v # vxx.xx.xのような形で表示されれば正解
$ npm -v # vx.x.xのような形で表示されれば正解

プロジェクトを作成する

プロジェクトを作成しましょう。ターミナルを開き、以下のコマンドを実行しましょう。

$ cd path/to/your/directory
$ npx create-react-app sample-atomic --template typescript

sample-atomicディレクトリが作成されていることを確認したら、以下のコマンドを実行して、アプリケーションを立ち上げましょう。

$ cd sample-atomic
$ npm start

以下の画面が表示されれば成功です!
スクリーンショット 2019-12-21 20.30.53.png

Atomic Designを導入する

必要なフォルダを作成し、routingのためのライブラリをインストールします。

$ mkdir src/components
$ mkdir src/components/atoms
$ mkdir src/components/molecules
$ mkdir src/components/organisms
$ mkdir src/components/pages
$ touch src/components/pages/Home.tsx
$ npm install @types/react-router-dom

App.tsxを以下のように編集します。

App.tsx
import React from 'react';
import { BrowserRouter, Route } from 'react-router-dom';
import './App.css';
import Home from './components/pages/Home';

const App: React.FC = () => {
  return (
    <BrowserRouter>
      <Route exact path='/' component={Home}></Route>
    </BrowserRouter>
  );
}

export default App;

material-uiをインストールしてからHome.tsxを編集します。

$ npm i @material-ui/core
Home.tsx
import React, { useState } from "react";
import List from '@material-ui/core/List'
import ListItem from '@material-ui/core/ListItem'

const Home: React.FC = () => {
  const [todos, setTodos] = useState<string[]>(["test1", "test2", "test3"]);
  const [value, setvalue] = useState<string>("");

  const onChange = (e: React.ChangeEvent<HTMLInputElement>): void => {
    const value = e.target.value;
    setvalue(value);
  };

  const onSubmit = (e: React.FormEvent<HTMLFormElement>): void => {
    e.preventDefault();
    setTodos([...todos.concat(value)]);
  };

  return (
    <div>
      <header>
        <h1>This is Header</h1>
      </header>

      <List>
        {todos.map(todo => (
          <ListItem key={todo}>{todo}</ListItem>
        ))}
      </List>

      <form onSubmit={onSubmit}>
        <input type='text' onChange={onChange} />
        <input type='submit' value='submit' />
      </form>
    </div>
  );
};

export default Home;

以下のような画面が出てくれば正解です。

スクリーンショット 2019-12-22 8.59.58.png

このアプリケーション内にある要素をAtomic Designに沿って分解していきます。上記をorganismsの粒度のコンポーネントに分けると、

  • Header
  • List
  • Form

となり、その中でさらにmoleculesとatomsに分解していきます。結果的に、src/componentsは以下のようなディレクトリ構成になりました(templateは今回作っていません)。

├── components
│   ├── atoms
│   │   └── FormInput.tsx
│   ├── molecules
│   │   └── Form.tsx
│   ├── organisms
│   │   ├── FormWrapper.tsx
│   │   ├── Header.tsx
│   │   └── TodoList.tsx
└── └── pages
        └── Home.tsx
Home.tsx
import React, { useEffect, useState } from "react";
import Header from '../organisms/Header';
import TodoList from "../organisms/TodoList";
import FormWrapper from "../organisms/FormWrapper";

const Home: React.FC = () => {
  const [todos, setTodos] = useState<string[]>(["test1", "test2", "test3"]);
  const [value, setvalue] = useState<string>("");

  const onChange = (e: React.ChangeEvent<HTMLInputElement>): void => {
    const value = e.target.value;
    setvalue(value);
  };

  const onSubmit = (e: React.FormEvent<HTMLFormElement>): void => {
    e.preventDefault();
    setTodos([...todos.concat(value)]);
  };

  return (
    <div>
      <Header text='This is Header' />

      <TodoList todos={todos} />

      <FormWrapper onChange={onChange} onSubmit={onSubmit} />
    </div>
  );
};

export default Home;
organisms/Header.tsx
import React from "react";

interface IProps {
  text: string;
}

const Header: React.FC<IProps> = ({text}) => {
  return (
    <header>
      <h1>{text}</h1>
    </header>
  );
};

export default Header;
organisms/TodoList.tsx
import React from "react";
import List from '@material-ui/core/List'
import ListItem from '@material-ui/core/ListItem'

interface IProps {
  todos: string[];
}

const TodoList: React.FC<IProps> = ({todos}) => {
  return (
    <List>
      {todos.map(todo => (
        <ListItem key={todo}>{todo}</ListItem>
      ))}
    </List>
  )
}

export default TodoList;

organisms/FormWrapper.tsx
import React from "react";
import Form from "../molecules/Form"
import FormInput from "../atoms/FormInput"

interface IProps {
  onSubmit: (e: React.FormEvent<HTMLFormElement>) => void;
  onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
}

const FormWrapper: React.FC<IProps> = ({onSubmit, onChange}) => {
  return (
    <Form onSubmit={onSubmit}>
      <FormInput onChange={onChange} />
      <input type='submit' value='submit' />
    </Form>
  );
};

export default FormWrapper;
molecules/Form.tsx
import React from "react";

interface IProps {
  onSubmit: (e: React.FormEvent<HTMLFormElement>) => void;
}

const Form: React.FC<IProps> = ({ children, onSubmit }) => {
  return <form onSubmit={onSubmit}>{children}</form>;
};

export default Form;
atoms/FormInput.tsx
import React from "react";

interface IProps {
  onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
}

const FormInput: React.FC<IProps> = ({ onChange }) => {
  return <input type='text' onChange={onChange} />;
};

export default FormInput;

コンポーネント分解し終えた後に、Todoアプリが通常通り動けば成功です!

終わりに

今回のように非常に小さなアプリケーションでは、Atomic Designを採用するメリットは見えづらいですが、実際に実務で使う場合は先述のメリットを享受することができます。今回の記事で、Atomic Designという考え方と、大まかな雰囲気を知っていただければ幸いです。

Atomic DesignはReactと相性が良く、今後も使われていくデザインの概念だと思います。ReactやVueなどのコンポーネント指向のライブラリを使う場合は、是非一緒に採用してみてください。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした