Todoアプリを作る
これまでで画面作成と画面遷移、イベントについては作成できた。
Reactここまでの資料 を参考
実アプリを作るための基本知識としてはあと以下をマスターする必要がある
- formを使った入力値
- ajaxを使ったサーバアクセス
- リスト形式のコンポーネント
これらを一気にTodoアプリを作ることで説明する。
モックサーバの作成
ajaxを使ってgetとかpostとかしたいが、APIサーバを別途立てるのは大変。
ということで仮想APIサーバを内部で立てる json-server
というパッケージを使う。
まずはパッケージのインストール
npm install json-server
つぎにモックサーバーの設定をする。
mock/api/todo.js
を作成
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
を作成
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
を以下のように変更する
"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
を作成して以下のように記述
{
"development": {
"baseURL": "http://localhost:4000"
},
"stage": {
"baseURL": "https://xxxxxx.xxxx.net"
},
"production": {
"baseURL": "https://xxxxxx.xxxx.net"
}
}
src/config/index.js
を作成
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()]
: {}
}
これで以下のようにすると設定値を取得できるようになった
import { getConfig } from './config'
const {
baseURL
} = getConfig()
Ajaxアクセス
ajaxへのアクセスを簡易的に行う為 axios
というパッケージを利用する
まずはインストール。 axios
と一緒に lodash
もインストール( lodash
については後述)
npm install axios lodash
axios利用にあたりすこし改造したいので、 src/base/Axios/Axios.js
を作って以下のように記述
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
export { Axios } from './Axios'
そして src/base/index.js
を作り、base配下のモジュールをexport
export * from './Axios'
使う時は以下のような感じになる
import { Axios } from '../../base'
const addTodo = async () => {
const { error } = await Axios().post('/api/todo', {
...input
})
if (error) return
// 後続の処理
}
getする場合はこんな感じで使う
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 参照
いくつか例を記載
Arrayの重複をつぶす
const uniq = _.uniq([1, 2, 3, 3, 3]) // [1,2,3]
2つの配列で共通するものだけの配列を作成
intersection
を使うとできる
_.intersection([2, 1], [2, 3])
// => [2]
List Mapでも可能
_.intersectionBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x');
// => [{ 'x': 1 }]
sortしたmap
_(list)
_.orderBy(hanbaiTankaRows, ['tekiyo_start_date'], ['desc'])
.map((row, i) => (
<HanbaiTankaRow
key={i}
data={row}
/>
).value()
)
Objectの特定キーを取り除く
_.omit({ a: '1', b: '2', c: '3'}, ['a', 'c'])
// => { b: '2' }
// spread separatorでもできるけどね
逆に特定キーだけにするなら pick
上限件数をつけてmapする take
arrayに take
をつけると順にXX件だけ取得ができる
_(filteredRows)
.take(100)
.map((row, i) => (
<KumiaiinCard
{...row}
key={i}
setVisitor={setVisitor}
/>
))
.value()
他にも色々あるので、適宜ドキュメントを参照しながら利用を推奨
入力フィールド
入力フィールドを配置し、入力された値を取得するには onChange
や onBlur
のイベントで
eventオブジェクトを取得して、そこから値を引っ張ればOK
事前にstateを用意しておき、イベントで値を取得してstateに格納する
以下の例はvalidateも何もない最もかんたんな配置例
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を作る
今までの内容を踏まえ作成する。
仕上がりのイメージ
- Routeに
/todo
というURLを作成してtodoのページを表示 - ajaxでtodoの情報を取得して、一覧を表示するコンポーネント作成
- 入力フォームを用意してtodoの新規追加をするコンポーネント作成
全ソースは ここ