LoginSignup
579

More than 3 years have passed since last update.

2018 年 React と Redux のエコシステム総まとめ

Last updated at Posted at 2018-02-01

Angular などの JavaScript フレームワークは大規模向けに設計されており、標準で多くのエコシステムが組み込まれています。

React は単なる View ライブラリです。そのため View ライブラリを補完するためのエコシステムの選択が必須となります。
エコシステムを自由に組み合わせて開発者とプロジェクトに応じた理想的なフレームワークを作成する必要があるわけです。
これは、小規模アプリケーションから大規模アプリケーションまでの様々な要件やニーズに対応できる柔軟性が高いことを意味していますが、エコシステムを選択するためのコストが必要となります。

下記では、筆者が最低限、導入を検討する余地があると考える主要な React のエコシステムとその簡単な概要を記載しています。

筆者の独断と偏見で選択したエコシステムであることを考慮してお読みいただきたいです。

既知のエコシステム (ほぼ知ってるかも...) は読み飛ばした方が良いかと思います。

また、筆者自身、未熟のため間違った内容が多々あると思います。間違いを見つけた方は、コメントや編集リクエストを頂けると非常にありがたいです。

読者対象

React と Redux のチュートリアルを一通りやり、React と Redux でアプリケーションを開発できる方を対象としています。
初心者の方も大歓迎ですが、HOC を書けるようになる前に Recompose を導入したり、ActionCreator や Reducer を書けるようになる前に redux-actions を導入したりすることは避けた方が良いです。

React

もっと多くの React のエコシステム以外も知りたい方はこちらを参照してください。

Boilerplate

React の開発環境のセットアップを自力で行いたくない場合は、Boilerplate を利用するのが手っ取り早いです。
小規模から大規模向けまで、数多くの Boilerplate が存在しています。

Create React App

既知の方が多いと思いますが、Create React App は、Facebook 製 React Boilerplate です。
ゼロ設定で React プロジェクトを始めることができます。
依存関係や設定を全て隠蔽してくれるのが特徴です。

以下のように簡単にプロジェクトを始めることができます。

npx create-react-app my-app
cd my-app
npm start

ユーザーガイドに従うことで、CSS プリプロセッサの追加や Flow の追加など、ある程度カスタマイズできます。

ユーザーガイドに記載されていないカスタマイズ (webpack の設定など) は、eject する必要があります。
eject することで隠蔽されている依存関係や設定を全て書き出すことができます。

Next.js

Next.js は、Zeit 製 React Boilerplate です。
SSR (Server Side Rendering) 機能も持つ Universal JavaScript プロジェクトを始めることができます。
SSR が必要な場合は、Create React App より Next.js を選択した方が良いでしょう。

Create React App 程ではないですが、以下のように簡単にプロジェクトを始めることができます。

npm install --save next react react-dom

package.json

{
  "scripts": {
    "dev": "next",
    "build": "next build",
    "start": "next start"
  }
}

./pages/index.js

export default () => <div>Welcome to next.js!</div>
npm run dev

同じく Zeit 製の Now を利用することで、React アプリケーションを素早くデプロイすることができます。

Gatsby

Gatsby は、React ベースのモダンな静的サイトジェネレーターです。
静的なサイトや、Wordpress のような本格的な CMS を簡単に構築することができます。

Gatsby は以下のような特徴があります。

  • React.js や webpack、モダンな JavaScript や CSS などの最新の Web テクノロジー。
  • ヘッドレス CMS、SaaS サービス、API、データベース、ファイルシステムなどのデータを GraphQL を使用して取得する。
  • 静的 PWA (Progressive Web Apps) を生成できる。
  • 最速の Web サイトを構築する。

Gatsby を使用した際の速さを公式ページから感じてください!
React の公式ページ なども Gatsby を使用して構築されています。

以下のようにプロジェクトを始めることができます。

npm install --global gatsby-cli
gatsby new gatsby-site
cd gatsby-site
gatsby develop

Utilities

JavaScript の ES6 以降のネイティブ機能として、配列やオブジェクト、数値、文字列などを扱う関数が多数用意されています。
React においては、リストを扱う際に、map() 関数をよく利用したりするでしょう。
筆者は、Lodash などのユーティリティライブラリは必要ないとまでは言えませんが、極力利用したくはないタイプのポケモンです。
ES6 以降のネイティブ関数だけではカバーできない場合は、Lodash または Ramda の使用をお勧めします。

Lodash

Lodash は、最も広く普及しているモダンな JavaScript ユーティリティライブラリです。

Ramda

Ramda は関数型プログラミングスタイルのために設計された JavaScript ユーティリティライブラリです。
関数型プログラミングが好きで、関数型プログラミングでアプリケーションを開発したい場合は Ramda をお勧めします。

Styling

React のスタイリングにおいて、CSS in JS の基本概念を抑えておく必要があります。

CSS in JS とは、 JavaScript を活用して以下のような CSS の多くの課題を解決するアプローチです。

大規模化する CSS の問題点

  1. グローバルネームスペース
  2. 依存性
  3. デッドコードの削除
  4. 縮小
  5. 定数の共有
  6. 非決定論的な解決
  7. 分離

React のスタイリングは、以下のように、CSS 定義を JavaScript オブジェクトで定義し、インラインスタイルで記述します。
style 属性に CSS をインラインで展開することにより、ローカルスコープを実現しています。

const Button = ({ children }) => (
  <button style={{
    background: 'transparent',
    color: 'palevioletred',
  }}>
    {children}
  </button>
);

しかし、インラインスタイルは、擬似クラスや擬似要素、メディアクエリの定義はできないという問題があります。

この問題を解決している便利な CSS in JS ライブラリとして CSS Modules と Styled Components をお勧めします。Styled Components は最近特に人気があります。

CSS Modules

CSS Modules は、全てのクラス名がデフォルトでローカルスコープされる CSS ファイルです。もしくは、それを扱うための仕組みといったところでしょう。

Reactを使ったモジュラーCSS : CSS-in-JSとCSS Module | プログラミング | POSTD

これは、CSSモジュールの非常にシンプルな前提です。ReactコンポーネントはそれぞれのCSSファイルを取得し、そのCSSファイルはそのファイルとそのコンポーネントにスコープされています。構築時に、魔法が起こります。非常にシンプルで衝突の危険がないローカルクラス名が、自動生成された名前にマッピングされ、React内で使うためのJSオブジェクトリテラルとしてエクスポートされます。

/* Thumbnail.css */
.image {
  border-radius: 3px;
}

/* Thumbnail.jsx */
import styles from './Thumbnail.css';
render() { return (<img className={styles.image}/>) }

/* Rendered DOM */
<img class="Thumbnail__image___1DA66"/>

/* Processed Thumbnail.css */
.Thumbnail__image___1DA66 {
  border-radius: 3px;
}

CSS Modules によって BEM の命名規則による慣習的ローカルスコープ化やクラス名の複雑性の問題は解決されます。

CSS Modules を実装したい場合は、以下のように webpack の css-loader のモジュールを有効にするだけです。

webpack.config.js

loaders: ['style-loader', 'css-loader?modules']

以下は、CSS Modules を利用する前後のコンポーネントのサンプルです。

Before:

BEM の命名規約を用いてローカルスコープ化されたクラス名を命名しています。

Button.css

.btn { /* ... */ }
.btn--success { /* ... */ }
.btn--danger { /* ... */ }
.btn--warning { /* ... */ }
.btn--info { /* ... */ }

Button.jsx

const Button = ({ size, children }) => (
  <button className='btn btn--success'>
    {children}
  </button>
);

After:

グローバルスコープの問題を恐れること無く、命名規約なしに好きなクラス名を命名できます。

Button.css

.normal { /* ... */ }
.success { /* ... */ }
.danger { /* ... */ }
.warning { /* ... */ }
.info { /* ... */ }

Button.jsx

import styles from './style.css';

const Button = ({ size, children }) => (
  <button className=`${styles.normal} ${styles.success}`>
    {children}
  </button>
);

Styled Components

Styled Components は React コンポーネントとスタイルをまとめて記述できるようにする新たなライブラリです。
ES6 のタグ付きテンプレートリテラル と ネスティングなどを含めた CSS 全機能 を組み合わせてコンポーネントをスタイリングします。

CSS Modules は className を使用してコンポーネントとスタイルをマッピングする必要がありましたが、Styled Components はその必要がありません。

以下は、Styled Components を利用する前後のコンポーネントのサンプルです。

Before:

Button.jsx

const Button = ({ primary, children }) => (
  <button style={{
    borderRadius: '3px',
    padding: '0.25em 1em',
    margin: '0 1em',
    background: primary ? 'palevioletred' : 'transparent',
    color: primary ? 'white' : 'palevioletred',
    border: '2px solid palevioletred',
  }}>
    {children}
  </button>
);

After:

Props を Styled Components に渡すことができたり、再利用できたりと様々な機能を提供しています。

Button.jsx

import styled, { css } from styled-components;

const Button = styled.button`
  border-radius: 3px;
  padding: 0.25em 1em;
  margin: 0 1em;
  background: transparent;
  color: palevioletred;
  border: 2px solid palevioletred;

  ${props => props.primary && css`
    background: palevioletred;
    color: white;
  `}
`;

Classnames

Classnames は classNames を条件付きで結合するためのシンプルな JavaScript ユーティリティです。
これは、React の 公式 Addons だった classSet の代替です。

以下は、Classnames を利用する前後のコンポーネントのサンプルです。

Before:

条件付きクラスを含む場合は、if 文で制御する必要があります。

Button.jsx

const Button = ({ size, children }) => {
  let classNames = ['btn'];

  if (size === 'large') {
    classNames.push('btn-lg');
  }

  if (size === 'small') {
    classNames.push('btn-sm');
  }

  return (
    <button className={classNames.join(' ')}>
      {children}
    </button>
  );
};

After:

条件付きクラスをオブジェクトとして表現できるためシンプルになります。

Button.jsx

import classNames from 'classnames';

const Button = ({ size, children }) => {
  const classNames = classNames({
    'btn': true,
    'btn-lg': size === 'large',
    'btn-sm': size === 'small'
  });

  return (
    <button className={classNames}>
      {children}
    </button>
  );
};

Classnames は CSS Modules 及び Styled Components にも使用することができます。

Type Checking

React での型チェックでは、PropTypes という機能を使います。
PropTypes は、コンポーネントの Props を型チェックします。

以下のように記述します。

import PropTypes from 'prop-types';

class Greeting extends React.Component {
  render() {
    return (
      <h1>Hello, {this.props.name}</h1>
    );
  }
}

Greeting.propTypes = {
  name: PropTypes.string
};

間違った型の Props がコンポーネントに渡されると、アプリケーションの実行中にエラーメッセージを表示します。

静的型チェッカーを使用することで、コードを実行する前に型の問題を特定できます。また、自動補完などの機能を追加することで、開発者のワークフローを改善することもできます。

Flow と TypeScript どちらを導入するか選択する際は、こちらの記事が役立つかもしれません。

Flow

Flow は、Facebook 製の JavaScript コードの静的型チェッカーです。変数宣言や、関数の引数や返り値に型定義 (アノテーション) することで、事前に型チェックすることができます。

flow-remove-types でアノテーションを取り除くこともできます。

TypeScript

TypeScript は、Microsoft 製の altJS です。これは、JavaScript の静的型付きスーパーセットであり、独自のコンパイラを含みます。

DefinitelyTypedflow-typed を比較すると一目瞭然なのですが、サードパーティライブラリの型定義ファイルが TypeScript の方が圧倒的に充実しています。

State Management

React は、State を保持する this.state と、State を更新する this.setState() を使い、コンポーネント内で State を管理する (ローカル State) ことができます。
基本的に、State を配置するコンポーネントはState を必要とする全てのコンポーネントの最上層にいる単一のコンポーネントです。

アプリケーションがスケールしていき複雑化することで、かつてないほどの多くの State を管理しなければなりません。
これを解決するために、Redux や MobX のような State を外出しして一元管理する (グローバル State) ライブラリを使用することをお勧めします。
グローバル State 管理ライブラリが必要かどうかはこちらを参照して決定してください。

Redux と MobX どちらを導入するか選択する際は、こちらの記事が役立つかもしれません。

Redux

Redux は React Community によって作られた State 管理ライブラリです。
React の State 管理ライブラリの中で最も人気があります。

Redux に React をブリッジングする場合は、React Redux を使用します。

2017年は Facebook 製の API 用のクエリ言語である GraphQL が話題になりました。
Facebook はもちろん GitHub などの大企業が GraphQL を使っています。

GraphQL を使用したい場合は、GraphQL のデータを取得し、そのデータを React アプリケーションに効率的に渡せるようにする GraphQL クライアントフレームワークを使用することをお勧めします。それは、Facebook 製の Relay もしくは Apollo が該当します。

こちらの記事に記載されている通り、Redux と Relay (もしくは Apollo) は相互に排他的である必要はありません。
それらを一緒に使用することで、Web 開発において非常に重要な関心の分離を提供することができます。
Redux はアプリケーションの State を保ち、アプリケーションがイベントにどのようにレスポンスするかを定義するのに役立ちます。Relay は GraphQL のデータの取得と更新ができます。

MobX

MobX はシンプルでスケーラブルな State 管理ライブラリです。
Redux より覚える量が少なく記述するコードの量が少なくて済むため、最近注目を集めています。
そのため、Redux と比べると自由度が高いため大規模なアプリケーション開発にはあまり向いていません。

MobX に React をブリッジングする場合は、mobx-react を使用します。

HOC (Higher-Order Components)

HOC は、高階関数ならぬ高階コンポーネントです。
つまり、コンポーネントを受け取って新しいコンポーネントを返す関数です。
コンポーネント間のクロスカットコンサーン (横断的関心事) を処理する Mixin の代替として利用されていたりします。
コンポーネントロジックを抽象化し再利用したい時や SFC (Stateless Functional Components) にライフサイクルメソッドを追加したい時など有用です。

HOC は React API の一部ではなく、React の構成上の性質から生まれるパターンです。

function higherOrderComponent(WrappedComponent) {
  return class extends React.Component {
    render() {
      return <WrappedComponent {...this.props}/>;
    }
  }
}

const EnhancedComponent = higherOrderComponent(WrappedComponent);

Recompose

A Note from the Author (acdlite, Oct 25 2018):

Hi! I created Recompose about three years ago. About a year after that, I joined the React team. Today, we announced a proposal for Hooks. Hooks solves all the problems I attempted to address with Recompose three years ago, and more on top of that. I will be discontinuing active maintenance of this package (excluding perhaps bugfixes or patches for compatibility with future React releases), and recommending that people use Hooks instead. Your existing code with Recompose will still work, just don't expect any new features. Thank you so, so much to @wuct and @istarkov for their heroic work maintaining Recompose over the last few years.

acdlite/recompose: A React utility belt for function components and higher-order components.

Recompose は、HOC のための React ユーティリティライブラリです。
React 用 Loadash ライクなライブラリと考えて問題ないようです。

例えば、リストを表示する PostsList という SFC にライフサイクルを追加したい場合は、以下のように記述します。

const PostsList = ({ posts }) => (
  <ul>{posts.map(p => <li>{p.title}</li>)}</ul>
)

const PostsListWithData = lifecycle({
  componentDidMount() {
    fetchPosts().then(posts => {
      this.setState({ posts });
    })
  }
})(PostsList);

他にも、Props を加工したり、State や Handler を追加したりする便利な関数が用意されています。

Routing

URL を変更してビューを切り替える必要がない場合は、条件付きレンダリングで十分です。
しかし、それは本当に小さい React アプリケーションにのみ当てはまる要件でしょう。
おそらくほとんどの開発者は React Router のようなルーティングライブラリが必要です。

React Router

React Router は React 用のルーティングライブラリです。

React Router v4 から react-router の代わりに以下のライブラリをインストールする必要があることに注意してください。これらをインストールすると、react-router は依存関係としてインストールされます。

React Web アプリケーション React Native アプリケーション
react-router-dom react-router-native

React Router を学びたい場合はこちらを参照してください。

Testing

Jest

Jest は Facebook 製の JavaScript テストフレームワークです。
テストの環境はもちろん、テストの構造からアサーション機能、モック、スパイ、スタブなどのテストするための基本的な機能を全て提供しています。
スナップショットテストやコードカバレッジレポートを生成することができます。

余談ですが、E2E テストツールといえばみんな大好き? Selenium ですよね。
Trending repositories on GitHub に、Selenium WebDriver に Jest テストをコネクトする Jest WebDriver Integration なんていうのがランクインしていました。現在開発中らしいですが、完成が楽しみですね!

Enzyme

Enzyme は Airbnb 製 の React コンポーネントをテストするためのユーティリティです。
Enzyme の API は、DOM 操作とトラバーサルのための jQuery の API を模倣することにより、直感的で柔軟性があります。
そのため、React 公式でも推奨しています。

低レベルな API で十分な場合は ReactTestUtils で十分ですが、より高レベルな API を使用したい場合は Enzyme の使用をお勧めします。

Linter

Linter は静的コード解析ツールです。
JavaScript のコードスタイルを強制したり、エラーや潜在的な問題を検出することができます。

ESLint

ESLint は JavaScript Linter です。
類似ツールとして JSLintJSHint があります。

現状、JavaScript Linter の中では npmtrendsGitHub Star History を参照して分かる通りデファクトスタンダードです。

ESLint の Shareable Config である eslint-config-airbnb を利用することで Airbnb の JavaScript スタイルガイドのルールに則っているか解析できます。
Shareable Config はこちらから選択することができます。

stylelint

stylelint は ESLint ライクな CSS Linter です。言うなれば、CSS 版 ESLint です。
類似ツールとして CSSLintCSScomb があります。

現状、CSS Linter の中では npmtrendsGitHub Star History を参照して分かる通りデファクトスタンダードです。

ESLint 同様、stylelint でも Shareable Config を利用することができます。
Shareable Config はこちらから選択することができます。

CSS Modules 及び Styled Components でも利用することができます。

Code Formatter

Prettier

Prettier は JavaScript や JSX を始めとした様々な言語の Code Formatter です。
prettier-eslint を利用することで ESLint と連携することもできます。

以下は、Prettier によるコードのフォーマット前後の結果です。

Before:

foo(reallyLongArg(), omgSoManyParameters(), IShouldRefactorThis(), isThereSeriouslyAnotherOne());

After:

foo(
  reallyLongArg(),
  omgSoManyParameters(),
  IShouldRefactorThis(),
  isThereSeriouslyAnotherOne()
);

Dev Tools

React Developer Tools

React Developer Tools

React Developer Tools は、コンポーネントの Props や State を含む React コンポーネントの階層をインスペクトするためのツールです。
Chrome extensionFirefox extensionStandalone app (Safari, React Native, etc) にも対応しています。

React Performance Devtool

React Performance Devtool

React Performance Devtool は、Reactコンポーネントのパフォーマンスをインスペクトするためのツールです。
使用されていないコンポーネントを確認したり、どのコンポーネントの読み込みに時間がかかるか調べたりすることができます。
Chrome extensionFirefox extension に対応しています。Standalone app については近日公開予定です。

UI Components

ボタンや、テキストボックス、テーブル、Date ピッカーなどの UI コンポーネントを一から実装したくない開発者向けに、既に数多くの UI コンポーネント が存在しています。
そのため、お好きな UI コンポーネントを選択すべきだと思いますが、あえて挙げるとするなら、Semantic UI と Material-UI、OnsenUI でしょうか。

Semantic UI

Semantic UI はテーマに合わせてデザインされた UI コンポーネントフレームワークです。

Material-UI

Material-UI は、Google が提唱する Material デザインな UI コンポーネントフレームワークです。

OnsenUI

OnsenUI

Onsen UI は、Javascript を使用して Android と iOS の両方で美しいハイブリッドとモバイルの Web アプリケーションを作成するのに役立つ UI コンポーネントライブラリです。

Onsen UIとは?

Onsen UIは、下のような特徴を持つライブラリです。

  • モバイルアプリの開発に特化したUIコンポーネントの集まりです。
  • ネイティブなiOSとAndroidのデザインガイドに準拠したデザインと機能を持っています。
  • 無料で利用でき、完全にオープンソースなソフトウェアです(Apache v2 license)。

Onsen UIを使うと、モバイルWebアプリの上でネイティブUIと同等のUX(ユーザー・エクスペリエンス)を提供することができます。モバイルWebアプリ(Progressive Web Appsとも呼ばれます)や、Cordovaを用いたハイブリッドアプリ開発に最適です。

筆者は業務で Cordova を利用したネイティブアプリケーション開発を行っているということもあり、今年最も利用したい UI コンポーネントです。

React Native にも NativeBase というハイブリッド UI コンポーネントがあります。

Components Workbenches

Storybook

Storybook

Storybook は、UI コンポーネントを定義、開発、およびテストするための開発環境です。
類似ツールと比較すると圧倒的な人気です。
この人気の裏には、React 以外にも React Native や Vue、 Angular もサポートや、コンポーネントの設計、ドキュメンテーション、テスト、インタラクティブ機能などのための多くの Addons の存在があります。

以下のように、JavaScript で UI コンポーネントを定義します。

import React from 'react'
import { storiesOf } from '@storybook/react'
import { action } from '@storybook/addon-actions'
import Button from '../components/Button'

storiesOf('Button', module)
  .add('default', () => (
    <Button onClick={action('clicked')}>Push Me</Button>
  ))
  .add('large size', () => <Button size="large">Push Me</Button>)

よく比較される類似ツールとして、Styleguidist があります。

Styleguidist

公式ドキュメントに 「React Styleguidist と Storybook との違い」 が記載されています。
ここに記載されている比較表には騙されないで下さい。
Styleguidist と Addons 無しの Storybook との比較です。

Storybook より Styleguidist があたかも優れているように見えますが Addons を利用することで、No の項目のほとんどが Yes になります。

Storybook との大きな違いは、Styleguidist は以下のように Markdown で UI コンポーネントを定義することでしょう。

React button component example:

```js 
<Button onClick={() => console.log('clicked')>Push Me</Button>
``` 

Large size:

```js 
<Button size="large">Push Me</Button>
``` 

Styleguidist は、ソースコードの JSDoc コメントブロックや PropTypes の宣言を react-docgen を解析してドキュメントに自動反映させるため、スタイルガイドの作成という面から見れば強力です。
しかし、Storybook はコンポーネントを開発するための Addons が豊富に用意されているため、UI コンポーネントの開発環境という面から見れば強力です。

また、類似ツールとして BluekitCatalog なども存在します。
Netflix のエンジニアリングチームが Storybook の代替案として Bluekit を採用して使用しているらしいです。

それぞれのツールの比較は「React Components Living Style Guides Overview | nearForm」を参照して下さい。
若干情報が古いため、改善されているデメリット (CONS) の項目があります。

React Studio

React Studio は GUI で設計したコンポーネントを React コンポーネントに変換することができるツールです。
詳しくは、少し前に話題になったこちらの記事を参照してください。

夢のようなツールですが、実際に触ったことがないため実務で利用できるかは疑問です。
Styled Components や UI Components などに対応しているのでしょうか?
時間があれば触ってみたいと思います。

Redux

もっと多くの Redux のエコシステム以外も知りたい方はこちらを参照してください。

Utilities

Reselect

Reselect は React Community によって作られたシンプルな Selector ライブラリです。
不要な再レンダリングや再計算を防ぎ、パフォーマンスを向上させることができます。

Redux Store の State を計算 (フィルタリングなど) して Props としてコンポーネントに渡す際、以下のように mapStateToProps 関数を利用すると思います。

// 計算
const getFilteredList = (list, filter) => {
  // filter の値に応じて list をフィルタリングして返す
}

const mapStateToProps = (state) => {
  return {
    list: getFilteredList(state.list, state.filter)
  }
}

この時、State ツリー ( state ) で変更が発生した場合、計算で使用しない無関係な部分 (state.liststate.filter 以外) の変更でも再計算されてしまいます。
Reselect を使用すると、State ツリーの関係ある部分 (state.liststate.filter) に変更が加わった時だけ再計算できます。
これにより、パフォーマンス上の問題を解決することができます。

詳しくは、こちらの記事を参照してください。

Nomalizr

多くの API は頻繁に深くネストされた JSON オブジェクトをレスポンスします。
Normalizr はスキーマのタイプとリレーションを定義し、スキーマとレスポンスデータを渡すことで正規化されたデータを出力できます。
その出力を Action に含め、ストアの更新に使用することで State をフラットに保つことができます。

正規化することで、以下のような懸念事項を解決することができます。

  • データがいくつかの場所で複製されると、適切に更新されることを確認するのがより困難になります。
  • ネストされたデータは、対応する Reducer ロジックがよりネストされることでより複雑にする必要があることを意味します。特に、深くネストされたフィールドを更新しようとすると、非常に醜いものになります。
  • イミュータブルデータ更新では、State ツリー内のすべての祖先をコピーして更新する必要があり、新しいオブジェクト参照によって接続された UI コンポーネントが再レンダリングされるため、深くネストされたデータオブジェクトを更新すると、表示されているデータが実際には変更されていない場合でも再レンダリングされます。

以下は、Nomalizr を使用してブログ記事のデータを正規化するサンプルです。

以下は、正規化するための originalData です。

{
  "id": "123",
  "author": {
    "id": "1",
    "name": "Paul"
  },
  "title": "My awesome blog post",
  "comments": [
    {
      "id": "324",
      "commenter": {
        "id": "2",
        "name": "Nicole"
      }
    }
  ]
}

article というエンティティタイプと、それにネストされた userscomments という2つのエンティティタイプを持っています。
schema オブジェクトを使用してスキーマを定義し、nomalize 関数を使用することで3つのエンティティタイプを全て正規化できます。

import { normalize, schema } from 'normalizr';

// users スキーマを定義
const user = new schema.Entity('users');

// comments スキーマを定義
const comment = new schema.Entity('comments', {
  commenter: user
});

// article スキーマを定義
const article = new schema.Entity('articles', {
  author: user,
  comments: [ comment ]
});

const normalizedData = normalize(originalData, article);

normalizedData は以下のようになります。

{
  result: "123",
  entities: {
    "articles": {
      "123": {
        id: "123",
        author: "1",
        title: "My awesome blog post",
        comments: [ "324" ]
      }
    },
    "users": {
      "1": { "id": "1", "name": "Paul" },
      "2": { "id": "2", "name": "Nicole" }
    },
    "comments": {
      "324": { id: "324", "commenter": "2" }
    }
  }
}

denormalize 関数を使用することで正規化されたデータを非正規化することができます。
正規化されたデータ (State) はコンポーネントで扱いづらいため、非正規化することをお勧めします。
正規化されたデータを大量にネストされたオブジェクトに戻すことで、アプリケーションのパフォーマンスが低下する恐れがあることにご注意ください。

Immutable.js

Immutable.js は、Facebook 製のイミュータブル (不変) なデータ構造を扱うためのライブラリです。

Immutable.js を選択する必要性 として以下の項目が挙げられます。

  • 不変性を保証
  • 豊富な API
  • パフォーマンス

React で不変性を扱うことの長所と短所について知りたい方は、こちらを参照してください。

Actions

redux-actions

redux-actions は Flux Standard Action のユーティリティです。

Flux Action オブジェクトの構造をどうするかは開発者に委ねられます。
この自由な構造に対してヒューマンフレンドリーな標準規約を設けたものが FSA (Flux Standard Action) になります。

FSA は以下のような構造となっています。

Basic:

{
  type: 'ADD_TODO',
  payload: {
    text: 'Do something.'
  }
}

Error:

{
  type: 'ADD_TODO',
  payload: new Error(),
  error: true
}

FSA のユーティリティ関数として、Action が FSA に準拠しているか確認する isFSA(action) と、Action がエラーか確認する isError(action) があります。
筆者は主に ActionCreator のテストコードでこれらの関数を使用しています。

import { isFSA, isError } from 'flux-standard-action';

redux-actions はこの FSA に準拠するためのユーティリティです。
これにより、プロジェクト内で Action の構造を統一することができます。

redux-actions は以下のように ActionCreator や Reducer を作成することができます。

import { createActions, handleActions, combineActions } from 'redux-actions'

const defaultState = { counter: 10 };

const { increment, decrement } = createActions({
  INCREMENT: amount => ({ amount }),
  DECREMENT: amount => ({ amount: -amount })
});

const reducer = handleActions({
  [combineActions(increment, decrement)](state, { payload: { amount } }) {
    return { ...state, counter: state.counter + amount };
  }
}, defaultState);

export default reducer;

Middleware

Async Actions

Redux Thunk

Redux Thunk は Action ではなく 関数を返す非同期な Action Creator を記述することができるようにする Redux ミドルウェアです。

以下のように、非同期な Dispatch を実行する関数を返す ActionCreator を記述できます。

function fetchData(someValue) {
  return (dispatch, getState) => {
    dispatch({type : "REQUEST_STARTED"});

    myAjaxLib.post("/someEndpoint", {data : someValue})
      .then(response => dispatch({type : "REQUEST_SUCCEEDED", payload : response})
      .catch(error => dispatch({type : "REQUEST_FAILED", error : error});
  };
}

function addTodosIfAllowed(todoText) {
  return (dispatch, getState) => {
    const state = getState();

    if(state.todos.length < MAX_TODOS) {
      dispatch({type : "ADD_TODO", text : todoText});
    }
  }
}
redux-saga

redux-saga/README_ja.md at master · redux-saga/redux-saga

redux-saga は React/Redux アプリケーションにおける副作用(データ通信などの非同期処理、ブラウザキャッシュへのアクセスのようなピュアではない処理)をより簡単で優れたものにするためのライブラリです。

Saga はアプリケーションの中で副作用を個別に実行する独立したスレッドのような動作イメージです。 redux-saga は Redux ミドルウェアとして実装されているため、スレッドはメインアプリケーションからのアクションに応じて起動、一時停止、中断が可能で、Redux アプリケーションのステート全体にアクセスでき、Redux アクションをディスパッチすることもできます。

以下のように、ジェネレーター関数を使用して非同期処理を同期的に記述できます。

function* fetchData(action) {
  const {someValue} = action;
  try {
    const result = yield call(myAjaxLib.post, "/someEndpoint", {data : someValue});
    yield put({type : "REQUEST_SUCCEEDED", payload : response});
  }
  catch(error) {
    yield put({type : "REQUEST_FAILED", error : error});
  }
}

function* addTodosIfAllowed(action) {
  const {todoText} = action;
  const todos = yield select(state => state.todos);

  if(todos.length < MAX_TODOS) {
    yield put({type : "ADD_TODO", text : todoText});
  }
}

詳しくは、こちらの記事を参照してください。

Routing

react-router-redux

react-router-redux は Reat Router と Redux の同期を保つライブラリです。URL と State を同期することができます。
Redux DevTools などのタイムトラベル機能を使用して、Action の記録、永続化、および再生に注意を払う場合に便利です。

Forms

Redux Form

Redux Form は Redux で Form の State を管理できるように実装する機能を提供するライブラリです。
Form のバリデーションも簡単に実装できます。

Dev Tools

Redux DevTools

Redux DevTools

Redux DevTools は、タイムトラベル UI の付いた Action ロガーです。
Action ペイロード及び State のロギングとそれらの確認ができます。
Reducer のためのホットリロード機能とエラーハンドリング機能があります。

以下のような Redux DevTools の Monitor を組み合わせることで、デバッグ環境を拡張することができます。

DevTools Monitors Overview
Log Monitor ツリー状の表示ができるデフォルトモニター。
Dock Monitor モニターのための、サイズ変更と移動が可能な Dock。
Slider Monitor 記録された Action を再生するカスタムモニター。
Inspector Action のフィルタリング、差分の調査、深くネストされた状態の変更監視のために状態のパスをピン留めしたりできるカスタムモニター。
Diff Monitor Action 間での Store の差分が取れるモニター。
Filterable Log Monitor Action 間での Store の差分が取れるモニター。
Chart Monitor チャート表示ができるモニター。
Filter Actions Action をフィルタリングでき、他のモニターを内包できるモニター。

Redux DevTools Extension

Redux DevTools Extension

Redux DevTools Extension は、Redux デバッグ機能を提供するブラウザーエクステンションです。

Redux DevTools をアプリに統合したくない場合はこのエクステンションをお勧めします。

Chrome extensionFirefox extension に対応しています。その他ブラウザーについては Remote Redux DevTools を使用することで対応することができます。

まとめ

エコシステムの選択に限った話ではありませんが、おそらくベターやベストあっても正解はありません。
プロジェクトの要件やニーズ、開発者のスキルや学習コストに応じて適切なエコシステムを選択し、良い React ライフをお送りください!

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
579