ReactでTodo

# Todoアプリを作る

これまでで画面作成と画面遷移、イベントについては作成できた。

[JavaScriptの基本](https://qiita.com/AkihiroTakamura/items/3149111aa2aca10ffd91) と

[Reactここまでの資料](https://qiita.com/AkihiroTakamura/items/75ba4cbdc6809bf68317) を参考

実アプリを作るための基本知識としてはあと以下をマスターする必要がある

- formを使った入力値
- ajaxを使ったサーバアクセス
- リスト形式のコンポーネント

これらを一気にTodoアプリを作ることで説明する。


## モックサーバの作成

ajaxを使ってgetとかpostとかしたいが、APIサーバを別途立てるのは大変。
ということで仮想APIサーバを内部で立てる `json-server` というパッケージを使う。

まずはパッケージのインストール

`npm install json-server`

つぎにモックサーバーの設定をする。

`mock/api/todo.js` を作成　

```javascript
module.exports = [
  {
    id: 1,
    title: '報告書の作成',
    detail: '6/30 AM中に送信',
    priority: 'high'
  },
  {
    id: 2,
    title: '洗濯',
    detail: null,
    priority: 'low'
  },
  {
    id: 3,
    title: 'tsutayaに返却',
    detail: 'DVD',
    priority: 'middle'
  }
]

```

`mock/server.js` を作成

```javascript
const jsonServer = require('json-server')
const server = jsonServer.create()

server.use((req, res, next) => {
  res.header('Access-Control-Allow-Origin', '*')
  res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, Authorization, Realm')
  res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS')
  next()
})

server.use('/api/todo', jsonServer.router({
  '': require('./api/todo'),
}))


if (module.parent === null) {
  var port = process.env.PORT || 4000
  server.listen(port)
  console.log('port:' + port)
}

module.exports = server
```

次に `package.json` を以下のように変更する


```javascript

"start": "react-scripts start",

↓

"start": "npx concurrently \"npm run mock\" \"react-scripts start\"",
"mock": "npx nodemon ./mock/server.js",

```

これで `npm start` すると Reactと一緒に json-serverもたちあがる。
`http://localhost:4000/api/todo` でアクセスできる

## 接続先などの環境変数を設定ファイルに外だしする

mock serverは `localhost:4000` で起動している。
ちゃんとしたAPIサーバを立てた場合は別サーバへの接続となるので
今後のために環境毎に接続先を切り替えられるように設定ファイルを外だしで定義しておく。

`src/config/config.json` を作成して以下のように記述


```json
{
  "development": {
    "baseURL": "http://localhost:4000"
  },
  "stage": {
    "baseURL": "https://xxxxxx.xxxx.net"
  },
  "production": {
    "baseURL": "https://xxxxxx.xxxx.net"
  }
}
```

`src/config/index.js` を作成

```javascript
import config from './config.json'

const hostname = window.location.hostname

export const getEnv = () => {
  return (hostname.indexOf('localhost') >= 0)
    ? 'development'
    : (hostname.indexOf('xxxx.xxxx.com') >= 0)
      ? 'stage'
      : 'production'
}

export const getConfig = () => {
  return (config && config[getEnv()])
    ? config[getEnv()]
    : {}
}
```

これで以下のようにすると設定値を取得できるようになった

```javascript

import { getConfig } from './config'

const {
  baseURL
} = getConfig()

```

## Ajaxアクセス

ajaxへのアクセスを簡易的に行う為 `axios` というパッケージを利用する

まずはインストール。 `axios` と一緒に `lodash` もインストール（ `lodash` については後述）

`npm install axios lodash`

axios利用にあたりすこし改造したいので、 `src/base/Axios/Axios.js` を作って以下のように記述

```javascript

import axios from 'axios'
import _ from 'lodash'
import { getConfig } from '../../config'

const {
  baseURL,
} = getConfig()

// ajax実行結果を簡単に取得できるように改造
axios.interceptors.response.use((response) => {
  return Promise.resolve({
    data: response.data,
    response,
  })
}, (error) => {
  return Promise.resolve({
    error: (error.response) ? error.response : error
  })
})

export const Axios = config => axios.create(_.assign({}, {
  baseURL,
}, config))

```

これを `src/base/Axios/index.js` でexport

```javascript
export { Axios } from './Axios'
```

そして `src/base/index.js` を作り、base配下のモジュールをexport

```javascript
export * from './Axios'
```

使う時は以下のような感じになる

```javascript
import { Axios } from '../../base'

const addTodo = async () => {
  const { error } = await Axios().post('/api/todo', {
    ...input
  })
  if (error) return
  // 後続の処理
}

```

getする場合はこんな感じで使う

```javascript
import React, { useState, useEffect } from 'react'
import _ from 'lodash'
import { Axios } from '../base'

const [list, setList] = useState([])

useEffect(() => {
  getTodo()
}, [
  refresh,
])

const getTodo = async () => {
  const { data, error } = await Axios().get('/api/todo', {})
  if (error) return
  setList(data)
}

return (
  <ul>
    {
      _(list)
        .map(row =>
          <li key={row.id}>
            <p>{row.id}</p>
            <p>{row.title}</p>
            <p>{row.detail}</p>
            <p>{row.priority}</p>
          </li>
        )
        .value()
    }
  </ul>

)

```


## lodashを使う

`lodash` はmapやfilterなどの配列操作や様々な関数を提供してくれる
ユーティリティ的なパッケージ。軽量。

`lodash` を使うと簡単にメソッドチェーンが組めたりするので非常に有用。多用する。

[詳細はdoc](https://lodash.com/docs/4.17.15) 参照

いくつか例を記載

### Arrayの重複をつぶす

```javascript

const uniq = _.uniq([1, 2, 3, 3, 3]) // [1,2,3]

```

### ２つの配列で共通するものだけの配列を作成

`intersection` を使うとできる

```javascript
_.intersection([2, 1], [2, 3])
// => [2]
```

List Mapでも可能

```javascript
_.intersectionBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x');
// => [{ 'x': 1 }]
```


### sortしたmap

```javascript
_(list)
  _.orderBy(hanbaiTankaRows, ['tekiyo_start_date'], ['desc'])
  .map((row, i) => (
    <HanbaiTankaRow
      key={i}
      data={row}
    />
  ).value()
)
```

### Objectの特定キーを取り除く

```javascript

_.omit({ a: '1', b: '2', c: '3'}, ['a', 'c'])
// => { b: '2' }
// spread separatorでもできるけどね

```

逆に特定キーだけにするなら `pick`


### 上限件数をつけてmapする `take`

arrayに `take` をつけると順にXX件だけ取得ができる


```javascript

_(filteredRows)
  .take(100)
  .map((row, i) => (
    <KumiaiinCard
      {...row}
      key={i}
      setVisitor={setVisitor}
    />
  ))
  .value()

```

他にも色々あるので、適宜ドキュメントを参照しながら利用を推奨


## 入力フィールド

入力フィールドを配置し、入力された値を取得するには `onChange` や `onBlur` のイベントで
eventオブジェクトを取得して、そこから値を引っ張ればOK

事前にstateを用意しておき、イベントで値を取得してstateに格納する

以下の例はvalidateも何もない最もかんたんな配置例

```javascript

const [title, setTitle] = useState('')
const [priority, setPriority] = useState('middle')

return (
  <>
    <label>タイトル</label>
    <input
      type='text'
      value={title}
      onChange={e => setTitle(e.target.value)}
      placeholder="Title"
    />

    <select
      value={priority}
      onChange={e => setPriority(e.target.value)}
    >
      <option value={'high'}>高</option>
      <option value={'middle'}>中</option>
      <option value={'low'}>低</option>
    </select>
  </>
)
```

## Todoを作る

今までの内容を踏まえ作成する。

仕上がりのイメージ

![](https://i.imgur.com/WdSSzmt.png)


- Routeに `/todo` というURLを作成してtodoのページを表示
- ajaxでtodoの情報を取得して、一覧を表示するコンポーネント作成
- 入力フォームを用意してtodoの新規追加をするコンポーネント作成


全ソースは [ここ](https://team.sbsnt.co.jp/gitbucket/takamura/react-training)