Edited at

Reactを使ったモダンなフロントエンド開発の環境構築


はじめに

Reactを中心としたフロントエンド開発において、以下のような構成を見かけることが多いと思います。


  • UIライブラリとしてReact

  • 型のある言語としてTypeScript

  • スタイル定義としてstyled-components

  • コンポーネントの開発環境としてStorybook

  • LinterとしてESLint

  • FormatterとしてPrettier

この記事では、各種ライブラリについて紹介したのち、それらを使う場合の環境構築についてハンズオン形式で説明します。

※ アプリケーションを開発する際に必要になる設定が抜けていたので、追記しました。


各種ライブラリの紹介

まず、各ライブラリがどのようなものなのかを簡単に紹介します。

ライブラリの使い方などは公式ドキュメントなどを参照するようにしてください。


React

ドキュメント

ReactはUI(ボタンやフォームなど)コンポーネントを作成するためのJavaScriptライブラリです。

似た立ち位置として、Vue.jsが上げられることも多いと思います。

公式ドキュメントが非常に充実しているので、初めての方は、Tutorialをやってみるのが良さそうです。


TypeScript

ドキュメント

TypeScriptは型を持ったJavaScriptのスーパーセットとして人気なプログラミング言語です。

コンパイルすると、JavaScriptのソースコードになります。

公式ドキュメント以外に、TypeScript Deep Diveが情報量が多く、分かりやすいと思います。


styled-components

ドキュメント

styled-componentsは、 CSSとほぼ同様のシンタックスを使ってスタイリングされたReactコンポーネントを作成できるライブラリです。

.cssファイルにCSSを書くのではなく、.js.tsxファイル内でCSSを書くようなイメージです。

公式ドキュメントを参照しながら、使ってみるのが良いと思います。


Storybook

ドキュメント

Storybookは、UIコンポーネントをアプリケーションとは独立した環境で開発できるツールです。

静的サイトとしてビルドすることで、コンポーネントのカタログとしても利用できます。

React用のチュートリアルも用意されています。


ESLint

ドキュメント

ESLintはJavaScriptのLinterです。

Linterを使うことで、コードの一貫性を高め、バグを未然に防げる可能性が高まります。

@typescript-eslint/eslint-pluginを使うことで、TypeScriptでも利用可能です。


Prettier

ドキュメント

Prettierはフロントエンド開発において人気なコードフォーマッターです。

JavaScriptをはじめとした複数の言語をサポートしています。

Prettierを使うことで、コードのフォーマットを意識することなく統一することができます。


環境構築

GitHubでリポジトリの作成から各種ライブラリのインストール、設定ファイルの作成などを行います。

最終的に、以下の事ができたらOKです。



  • npm run storybook: Storybookを起動して、React+TypeScript+styled-componentsで作成したコンポーネントが表示できる


  • npm run lint: ESLint + Prettierを使ってコードのチェックが行える


  • npm run tsc: TypeScriptのコードに対してコンパイルチェックが行える


Step0 事前準備

npm/Node.js/gitがインストールされていることを確認してください。

$ git --version

git version 2.20.1 (Apple Git-117)
$ node -v
v12.4.0
$ npm -v
6.9.0

バージョンを揃える必要はありませんが、必要に応じてLTSまたは最新版を利用するようにしましょう。


Step1 リポジトリの作成/git clone/gitignore

GitHubで今回作業をするリポジトリを作成しましょう。

新しいリポジトリを作成

リポジトリを作成後は、そのリポジトリをクローンしておいてください。

また、好きなgitignoreをリポジトリに配置しておきます。

例) Node.gitignoreを利用する場合

$ wget https://raw.githubusercontent.com/github/gitignore/master/Node.gitignore

$ mv Node.gitignore .gitignore

これでリポジトリの準備は整いました!


Step2 package.jsonを作成する

npmのinitコマンドを使って、package.jsonの雛形を作成しましょう。

$ npm init -y


Wrote to /dev/github.com/toshi-toma/todo-app-react/package.json:

{
"name": "todo-app-react",
"version": "1.0.0",
"description": "A Todo Application built with React, styled-components, TypeScript, E
SLint, Prettier, and Storybook",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+ssh://git@github.com/toshi-toma/todo-app-react.git"
},
"keywords": [],
"author": "",
"license": "ISC",
"bugs": {
"url": "https://github.com/toshi-toma/todo-app-react/issues"
},
"homepage": "https://github.com/toshi-toma/todo-app-react#readme"
}

npmのCLIには便利なコマンドがたくさん用意されています。ドキュメントをさっと見るだけでも面白いと思います。


Step3 Reactでコンポーネントを作成して、Storybookに表示する

Reactでコンポーネントを作成して、そのコンポーネントをStorybookに表示して動作や見た目を確認していくとスムーズな開発が行えます。

まずはReactとstyled-componentsでシンプルなコンポーネントを作成してみましょう。

必要なライブラリをインストールします。

$ npm i react react-dom  styled-components

...
+ react@16.9.0
+ react-dom@16.9.0
+ styled-components@4.3.2
added 11 packages from 12 contributors, updated 2 packages and audited 31753 packages in 5.34s

Reactとstyled-componentsでシンプルなボタンを作成してみましょう。

src/index.js

import React from "react";

import styled from "styled-components";

const Button = styled.button`
background-color: #454545;
color: #fff;
`
;

const SimpleButton = () => <Button>button</Button>; // 省略可能

export default SimpleButton;

次に、作成したボタンをStorybookで表示しましょう。

Googleで「Storybook React」と検索するとStorybook for Reactというタイトルの公式ドキュメントが出てくると思います。

Storybook for React

ドキュメントを読むと、Automatic setupコマンドが用意されているようです。

実行してみましょう。

$ npx -p @storybook/cli sb init --type react


...

+ @storybook/react@5.1.10
+ @storybook/addon-links@5.1.10
+ @babel/core@7.5.5
+ babel-loader@8.0.6
+ @storybook/addon-actions@5.1.10
+ @storybook/addons@5.1.10
added 1147 packages from 843 contributors and audited 31533 packages in 41.347s

...

• Installing dependencies. ✓

To run your storybook, type:

npm run storybook

For more information visit: https://storybook.js.org

これでStorybookの環境構築及びReactコンポーネントを表示する準備が整いました!簡単ですね。

npm run storybookを実行するとStorybookが表示されると思います。

あとは、先程作成したボタンをStorybookに表示しましょう。

stories/index.stories.js

import React from 'react';

import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';

import Button from '../src/index';

storiesOf('Button', module)
.add('with text', () => <Button onClick={action('clicked')}>Hello Button</Button>);

無事にReactとstyled-componentsで作成したコンポーネントをStorybookに表示することができました🎉


Step4 TypeScriptでコンポーネントを作成して、Storybookに表示する

次はReactとstyled-componentsに加えてTypeScriptを使ってコンポーネントを作成して、そのコンポーネントをStorybookに表示します。

まずはStep3で作成したコンポーネントをTypeScript化しましょう。

必要なライブラリをインストールします。

TypeScriptに加えて、型情報もインストールします。

$ npm i -D typescript

$ npm i -D @types/{react,react-dom,styled-components}

+ @types/styled-components@4.1.18
+ @types/react-dom@16.8.5
+ @types/react@16.9.1

TypeScriptの設定ファイルの雛形をtscコマンドで作成します。

$ npx tsc --init  

message TS6071: Successfully created a tsconfig.json file.

tsconfig.jsonというファイルが作成されたので、React用に編集します。

tsconfig.json

{

"compilerOptions": {
"target": "es5",
"module": "commonjs",
"lib": ["es2018", "dom"],
"jsx": "react",
"strict": true,
"rootDirs": [
"src"
],
"types": [
"react"
],
"esModuleInterop": true
},
"include": [
"./src/**/*"
],
"exclode": [
"node_modules/"
]
}

これでTypeScriptを使う準備が整ったので、コンポーネントをTypeScriptで書き換えてみます。

ReactとTypeScriptを使う場合のコンポーネントファイルの拡張子は.tsxになります。

mv src/index.js src/index.tsx

src/index.tsx

import React, { FC } from "react";

import styled from "styled-components";

const Button = styled.button`
background-color: #454545;
color: #fff;
`
;

interface Props {
onClick: () => void;
}

const SimpleButton: FC<Props> = () => <Button>Done</Button>;

export default SimpleButton;

最後に書き換えたコンポーネントをStorybookで表示します。

TypeScriptを使う場合、別途Storybookの設定が必要です。

必要な設定については、公式ドキュメントが用意されています。

ドキュメントを読むと、必要な設定が@storybook/preset-typescriptとして用意されているので、これを利用します。

必要なライブラリをインストールしましょう。

$ npm i -D @storybook/preset-typescript react-docgen-typescript-loader ts-loader 

$ npm i -D @types/{storybook__addon-actions,storybook__react}

...

+ ts-loader@6.0.4
+ react-docgen-typescript-loader@3.1.1
+ @storybook/preset-typescript@1.1.0

...

+ @types/storybook__addon-actions@3.4.3
+ @types/storybook__react@4.0.2

npm i -D xxxを実行したログに以下のようなWarningが表示されていると思います。

...

npm WARN @storybook/preset-typescript@1.1.0 requires a peer of @types/webpack@* but none is installed. You must install peer dependencies yourself

...

@storybook/preset-typescript@1.1.0@types/webpack@*をpeer dependencyとしているようです。

@storybook/preset-typescriptのリポジトリを開いて、package.jsonを見てみましょう。

preset-typescript/package.json

  ...

"peerDependencies": {
"@types/webpack": "*",
"react-docgen-typescript-loader": "*",
"ts-loader": "*"
}

peerDependenciesとして@types/webpackが指定されているので、インストールしておきましょう。

$ npm i -D @types/webpack

+ @types/webpack@4.32.1

あとは.storybook/presets.jsを作成して、.storybook/presets.js.storybook/config.jsを以下のように編集するだけです。

presets.js

module.exports = ["@storybook/preset-typescript"];

config.js

import { configure } from '@storybook/react';


// automatically import all files ending in *.stories.js
- const req = require.context('../stories', true, /\.stories\.js$/);
+ const req = require.context('../src', true, /\.stories\.tsx$/);
function loadStories() {
req.keys().forEach(filename => req(filename));
}

configure(loadStories, module);

storiesファイルの拡張子も.jsから.tsxに変更しましょう。

$ mkdir src/stories

$ mv stories/index.stories.js src/stories/index.stories.tsx
$ rm -rf stories

npm run storybookでコンポーネントが表示されたら成功です🎉

また、TypeScriptで書いたコードはコンパイルを実行することで静的型付けによる型チェックを行えます。

型チェックを行なうnpm scriptsを追加しておきましょう。

package.json

"scripts": {

...
+ "tsc": "tsc --noEmit",

$ npm run tsc

ここまでの作業によるpackage.jsonの差分を出しておきます。

   ...

"description": "A Todo Application built with React, styled-components, TypeScript, ESLint, Prettier, and Storybook",
"main": "index.js",
"scripts": {
- "test": "echo \"Error: no test specified\" && exit 1"
+ "test": "echo \"Error: no test specified\" && exit 1",
+ "tsc": "tsc --noEmit",
+ "storybook": "start-storybook -p 6006",
+ "build-storybook": "build-storybook"
},
"repository": {
"type": "git",
@@ -16,5 +18,28 @@
"bugs": {
"url": "https://github.com/toshi-toma/todo-app-react/issues"
},
- "homepage": "https://github.com/toshi-toma/todo-app-react#readme"
+ "homepage": "https://github.com/toshi-toma/todo-app-react#readme",
+ "dependencies": {
+ "react": "^16.9.0",
+ "react-dom": "^16.9.0",
+ "styled-components": "^4.3.2"
+ },
+ "devDependencies": {
+ "@babel/core": "^7.5.5",
+ "@storybook/addon-actions": "^5.1.10",
+ "@storybook/addon-links": "^5.1.10",
+ "@storybook/addons": "^5.1.10",
+ "@storybook/preset-typescript": "^1.1.0",
+ "@storybook/react": "^5.1.10",
+ "@types/react": "^16.9.1",
+ "@types/react-dom": "^16.8.5",
+ "@types/storybook__addon-actions": "^3.4.3",
+ "@types/storybook__react": "^4.0.2",
+ "@types/styled-components": "^4.1.18",
+ "@types/webpack": "^4.32.1",
+ "babel-loader": "^8.0.6",
+ "react-docgen-typescript-loader": "^3.1.1",
+ "ts-loader": "^6.0.4",
+ "typescript": "^3.5.3"
+ }
}


Step5 ESLintとPrettierでLintとFormatを導入する

TypeScriptのコードをESLintでLint、PrettierでFormatできるようにしましょう。

今回は手順をいくつか省略できるように、npmで公開されているeslint-configを利用します。

ここで利用するルールは自分の好みに応じてカスタマイズしてOKです。

npmの検索窓で「eslint typescript prettier」と検索してみましょう。

以下の手順では、airbnbが提供しているeslint-config-airbnbとTypeScript、Prettierのサポートが入ったeslint-config-airbnb-typescript-prettierを利用します。

eslint-config-airbnb-typescript-prettier

パッケージのページに書いてある説明を参考に、必要なライブラリをインストールします。

$ npm i -D eslint@^5.3.0 prettier@^1.18.2 eslint-config-airbnb-typescript-prettier

+ prettier@1.18.2
+ eslint@5.16.0
+ eslint-config-airbnb-typescript-prettier@1.2.1
added 106 packages from 69 contributors and audited 32518 packages in 10.364s

次にESLintの設定ファイルである.eslintrc.jsを作成して、以下のように設定を書きます。

.eslintrc.js

module.exports = {

extends: "airbnb-typescript-prettier"
};

ESLintの設定が終わったら、npm scriptsを追加しておきましょう。

package.json

"scripts": {

+ "lint": "eslint --ext .ts,.tsx src/",
+ "lint:fix": "eslint --ext .ts,.tsx src/ --fix",

これで次からnpm run lintを実行することで、srcディレクトリ以下のTypeScriptファイルに対してLintとFormatチェックが実行されます。

$ npm run lint


> eslint --ext .ts,.tsx src/

/dev/github.com/toshi-toma/todo-app-react/src/index.tsx
15:23 error Insert `⏎` prettier/prettier

/dev/github.com/toshi-toma/todo-app-react/src/stories/index.stories.tsx
1:19 error Replace `'react'` with `"react"` prettier/prettier
3:1 error '@storybook/react' should be listed in the project's dependencies, not devDependencies import/no-extraneous-dependencies

....

✖ 8 problems (8 errors, 0 warnings)
6 errors and 0 warnings potentially fixable with the `--fix` option.

FormatやいくつかのLintエラーは自動で修正が可能です。

npm run lint:fixを実行してみましょう。

$ npm run lint:fix


> todo-app-react@1.0.0 lint:fix /dev/github.com/toshi-toma/todo-app-react
> eslint --ext .ts,.tsx src/ --fix

/dev/github.com/toshi-toma/todo-app-react/src/stories/index.stories.tsx
3:1 error '@storybook/react' should be listed in the project's dependencies, not devDependencies import/no-extraneous-dependencies
4:1 error '@storybook/addon-actions' should be listed in the project's dependencies, not devDependencies import/no-extraneous-dependencies

✖ 2 problems (2 errors, 0 warnings)

自動で修正されました🎉

... should be listed in the project's dependencies, not devDependencies import/no-extraneous-dependenciesとエラーがでています。

ESLintの設定は必要に応じて調整しましょう。

以下のように設定を変更することで、エラーが直るはずです。

.eslintrc.js

module.exports = {

extends: "airbnb-typescript-prettier",
rules: {
"import/no-extraneous-dependencies": ["error", {
devDependencies: ["**/*.stories.tsx"],
peerDependencies: false
}]
}
};


Step6 VSCodeでファイル保存時に自動でフォーマットする

エディタにVSCodeを利用している人を想定して、ファイル保存時にフォーマットエラーなどの自動修正を行えるようにします。

まず、VSCodeのExtensionsでVSCode ESLintをインストールしておきましょう。

あとは、以下のように.vscode/settings.jsonを修正します。

{

"eslint.autoFixOnSave": true,
"eslint.validate": [
"javascript",
"javascriptreact",
{ "language": "typescript", "autoFix": true },
{ "language": "typescriptreact", "autoFix": true }
]
}


Webアプリの開発(追記)

前の章では、React、styled-components、TypeScript、ESLint、Prettierを使ってコンポーネントを開発して、それをStorybook上で表示するということを行いました。しかし、実際にWebアプリケーションを開発する際は、HTMLファイルを用意して、そこからJavaScriptファイルを読み込ませることになると思います。

この章では、そのために必要なライブラリや設定について紹介します。


Step0 index.htmlとindex.tsxの作成

まず事前準備として、ブラウザに読み込ませるHTMLファイルとアプリケーションの起点となるindex.tsxファイルを作成しておきましょう。

public/index.html

<!DOCTYPE html>

<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>todo-app-react</title>
</head>

<body>
<div id="root"></div>
<script src="bundle.js"></script>
</body>

</html>

src/index.tsx

import React from "react";

import ReactDOM from "react-dom";

// 実際は、ここでアプリケーションコンポーネントを読み込ませる
ReactDOM.render(<h1>ToDo App</h1>, document.getElementById("root"));


Step1 webpackで必要なJavaScriptファイルを生成する

webpackを利用して、index.htmlに読み込ませるJavaScriptファイルbundle.jsを生成しましょう。

必要なライブラリをインストールしましょう。

$ npm i -D webpack webpack-cli

次に、webpack.config.jsを作成して、必要な設定を書きましょう。

webpack.config.js


const path = require("path");

module.exports = {
entry: path.resolve(__dirname, "src/index.tsx"),

output: {
path: path.resolve(__dirname, "public"),
filename: "bundle.js"
},

module: {
rules: [{ test: /\.tsx?$/, loader: "ts-loader" }]
},

resolve: {
extensions: [".ts", ".tsx", ".js"]
}
};

※ ここでts-loaderが必要ですが、Storybookの環境構築時にインストールされているはずなので、割愛します。

あとは、bundle.jsを生成するnpm scriptsを追加して、実行します。

 "scripts": {

+ "build": "webpack --mode production",

$ npm run build

publicディレクトリに、bundle.jsというファイルが生成されました。

public/index.htmlを開くと、bundle.jsが読み込まれたページが表示されます。


Step2 webpack-dev-serverで開発用サーバーを立てて開発する

TypeScriptのファイルを変更するたびに、buildコマンドを実行するのは面倒なので、開発中はローカルに開発用サーバーをたてて、変更時にブラウザを自動でリロードできると楽です。ここではwebpack-dev-serverを利用して実現します。

ライブラリをインストールして、webpack.config.jsに設定を追記します。

$ npm i -D webpack-dev-server

webpack.config.js

const path = require("path");


module.exports = {
entry: path.resolve(__dirname, "src/index.tsx"),

...

resolve: {
extensions: [".ts", ".tsx", ".js"]
- }
+ },
+ devServer: {
+ contentBase: path.resolve(__dirname, "public")
+ }

};

あとは、開発用サーバーを起動するnpm scriptsを追加します。

"scripts": {

+ "start": "webpack-dev-server --mode development --open --devtool cheap-module-source-map",

npm startを実行すると、開発用サーバーが起動して、public/index.htmlが表示され、src/index.tsxを起点としたファイルが読み込まれます。


さいごに

これで


  • React+styled-components+TypeScriptでコンポーネントを開発

  • 作成したコンポーネントはStorybookで動作確認

  • 適宜ESLintやPrettierを利用してコーディングを行い、ファイル保存時に自動で修正

  • webpackやwebpack-dev-serverを利用してWebアプリの開発

が行えるようになり、環境構築終了です🎉


最終的なpackage.json

この記事における最終的なpackage.jsonを貼っておきます。



{
"name": "todo-app-react",
"version": "1.0.0",
"description": "A Todo Application built with React, styled-components, TypeScript, ESLint, Prettier, and Storybook",
"main": "index.js",
"scripts": {
"start": "webpack-dev-server --mode development --open --devtool cheap-module-source-map",
"build": "webpack --mode production",
"lint": "eslint --ext .ts,.tsx src/",
"lint:fix": "eslint --ext .ts,.tsx src/ --fix",
"tsc": "tsc --noEmit",
"storybook": "start-storybook -p 6006",
"build-storybook": "build-storybook"
},
"repository": {
"type": "git",
"url": "git+ssh://git@github.com/toshi-toma/todo-app-react.git"
},
"keywords": [],
"author": "",
"license": "MIT",
"bugs": {
"url": "https://github.com/toshi-toma/todo-app-react/issues"
},
"homepage": "https://github.com/toshi-toma/todo-app-react#readme",
"dependencies": {
"react": "^16.9.0",
"react-dom": "^16.9.0",
"styled-components": "^4.3.2"
},
"devDependencies": {
"@babel/core": "^7.5.5",
"@storybook/addon-actions": "^5.1.10",
"@storybook/addon-links": "^5.1.10",
"@storybook/addons": "^5.1.10",
"@storybook/preset-typescript": "^1.1.0",
"@storybook/react": "^5.1.10",
"@types/react": "^16.9.1",
"@types/react-dom": "^16.8.5",
"@types/storybook__addon-actions": "^3.4.3",
"@types/storybook__react": "^4.0.2",
"@types/styled-components": "^4.1.18",
"@types/webpack": "^4.32.1",
"babel-loader": "^8.0.6",
"eslint": "^5.16.0",
"eslint-config-airbnb-typescript-prettier": "^1.2.1",
"prettier": "^1.18.2",
"react-docgen-typescript-loader": "^3.1.1",
"ts-loader": "^6.0.4",
"typescript": "^3.5.3",
"webpack": "^4.39.2",
"webpack-cli": "^3.3.7",
"webpack-dev-server": "^3.8.0"
}
}