LoginSignup
5
9

More than 3 years have passed since last update.

ReactでTodo

Last updated at Posted at 2020-07-08

Todoアプリを作る

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

JavaScriptの基本

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()

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

入力フィールド

入力フィールドを配置し、入力された値を取得するには onChangeonBlur のイベントで
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の新規追加をするコンポーネント作成

全ソースは ここ

5
9
0

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
5
9