新規プロダクト開発でReactを導入するのに行ったこと


## はじめに

これは [Supership株式会社 Advent Calendar 2018](https://qiita.com/advent-calendar/2018/supership)の20日目の記事です。

私は普段こちらの記事に書かれている、新規プロダクトの開発にエンジニアとして携わっています。

* [1つのプロダクトを複数社で開発する現場にスクラムを導入した話](https://qiita.com/Kujou/items/66f640a48aa9777b8b41)
* [開発未経験者がスクラム開発PJのPMになって3ヶ月経って見えてきたもの](https://qiita.com/tg_qiita/items/47ac1fb2f66e3d849fe2)

今回は新規プロダクト開発でReactを導入するのに行ったことを書きます。

## TypeScript

型を使えることによって変更した時に壊れた部分が分かるので、もう普通のJavaScriptに戻れないぐらい便利ですね。

複数人でコードを書くことを考慮して、記法の統一や機械的にチェックできる使ったらまずい書き方を検出するために[TSLint](https://palantir.github.io/tslint/)と[Prettier](https://prettier.io/)を導入しました。

現状の設定はこのようにしてます。
この辺の設定は最初から全部無効にしてしまうと意味がないので最初はデフォルトの設定にしておいて運用してみてどうしても厳しい設定だけを無効にするのが良いと思います。

```json:tslint.json
{
  "defaultSeverity": "error",
  "extends": ["tslint:recommended", "tslint-config-prettier"],
  "jsRules": {},
  "rules": {
    "interface-over-type-literal": false,
    "object-literal-sort-keys": false,
    "member-access": false,
    "no-default-export": true,
    "prettier": [
      true, {
        "singleQuote": true,
        "semi": false,
        "printWidth": 128
      }
    ]
  },
  "rulesDirectory": ["tslint-plugin-prettier"]
}
```

## 非同期処理

Reduxのアクションを書く時にその場で直接結果が返らない同期処理の場合どうするかというのが悩ましく[redux-saga](https://github.com/redux-saga/redux-saga)を導入するなどの方法がありますが、それはそれでsagaのレイヤーが増えるのと、そもそも非同期の場合も同期の時と同じように扱いたかったため、非同期のアクションも書ける[hard-reducer](https://github.com/mizchi/hard-reducer)を導入しました。

### GraphQL APIを叩いてる例

```js
export const createGroup = createThunkAction(
  'create-group',
  async (variables: { input: CreateGroupInput }, dispatch, getState: () => RootState) => {
    const result = await client.mutate({
      mutation: gql`
        mutation CreateGroup($input: CreateGroupInput!) {
          createGroup(input: $input) {
            id
            name
            errors {
              message
              path
            }
          }
        }
      `,
      variables
    })
    const errors = result.data.createGroup.errors
    if (errors.length > 0) {
      dispatch(AlertActions.setErrors({ errors }))
    } else {
      dispatch(GroupActions.setGroup({ group: result.data.createGroup }))
    }
  }
)
```

# Storybook

https://storybook.js.org

* コンポーネントのデザインカタログが作れる
* コンポーネントだけ先に作成して、デザイン当てができる

のがすごく便利でした。
また開発時の確認環境にデプロイする時に静的ファイルに書き出して見れるようにしてます。

# CSS

### styled-components

https://www.styled-components.com

CSSの問題点として全部グローバルスコープというのがありまして、気をつけないとCSSクラス名などが簡単にぶつかります。
そのために[BEM](http://getbem.com/)や[SUIT CSS](https://suitcss.github.io/)のような名前がぶつからないような規約が必要になってたという経緯もあり、styled-componentsを使うことでstyled-componentsがぶつからないクラス名を振って、htmlの中にCSSを埋め込むようになるのでコンポーネントの中だけの閉じた状態でCSSが書けるようになります。

```tsx
import React from 'react'
import styled from 'styled-components'

type Props = {
  items: Array<{
    id: string
    name: string
  }>
}

function Items(props: Props) {
  return (
    <List>
    {items.map(item => {
      return (<ListItem key={item.id}>{item.name}</ListItem>) 
    })}
    </List>
  )
}

const List = styled.ul`
  color: red;
  margin-bottom: 10px;
`

const ListItem = styled.li`
  border: 1px solid green;
`
```

### styled-system

https://jxnblk.com/styled-system

コンポーネントを組み込む時に、ほとんど同じだけど色だけ変えたい、ちょっとだけ位置を調整したい場合はstyled-systemを組み合わせるとシンプルにできます。


```tsx
import React from 'react'
import styled from 'styled-components'
import { space, color } from 'styled-system'

type Props = {
  items: Array<{
    id: string
    name: string
  }>
}

function Items(props: Props) {
  return (
    <List>
    {items.map(item => {
      return (<ListItem key={item.id}>{item.name}</ListItem>) 
    })}
    </List>
  )
}

const List = styled.ul`
  ${color}
  ${space}
`

const ListItem = styled.li`
  border: 1px solid green;
`
```

### 色だけ変えたい

```tsx
<Items color="green" items={items} />
```

### 位置を微調整したい

```tsx
// margin-bottom: 5px
<Items mb={5} items={items} />
```

## i18n

多言語対応のJavaScriptのライブラリはいろいろありますが、標準的なものを使いたかったため、ECMAScript標準の多言語化APIの[Intl](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Intl)ベースでReactで使える[react-intl](https://github.com/yahoo/react-intl)を導入しました。

```tsx
import React from 'react'
import { FormattedMessage } from 'react-intl'

type Props = {}

function Label(props: Props) {
  return <div><FormattedMessage id="label.name" /></div>
}
```

## Recomponse

RecomponseはReactのコア機能のHooksに置き換わるので、新規の場合は使わないか将来的に移行する時を考慮して最低限にしておいたほうが良いと思います。

https://github.com/acdlite/recompose#a-note-from-the-author-acdlite-oct-25-2018

今開発中のプロダクトではStateless Functional ComponentsでcomponentDidMountを使いたい場合のみにしか使ってないです。

## 最後に

新規開発でこれからリリースに向けてやっていくフェーズでまだまだエンジニアを超募集してます。
私たちと働きたい方はこちらから応募お願い致します。

[バックエンドエンジニア（DMP事業）](https://rec-log.jp/site/jobVw.aspx?company_id=7125&job_id=203616)