はじめに
- 前回、BMIの計算フォームを作成しましたが、日付事に結果を残すものにバージョンアップする
px-wing.hatenablog.com
- 同じ日付を入力したら、以前のデータを上書きする
- reduxも利用してデータの持ち回りをする。日付事に一覧を表示させる
- 登録したデータを削除できるようにする
- 日付の新しい順からテーブルに一覧で表示させる
上記の実装をしてみた。
コード
- components/bmi/BmiForm.js
import React,{useState, useContext} from 'react'
import {Form, Button} from 'react-bootstrap'
import moment from 'moment'
import AppContext from '../../contexts/AppContext'
import {
CREATE_BMI,
DELETE_BMI,
} from '../../actions'
import Bmis from './Bmis'
import '../siginin.css'
import { Formik } from 'formik'
import * as yup from 'yup'
const BmiForm = () => {
const {state, dispatch } = useContext(AppContext)
const [bmi, setBmi] = useState({resultBmi: 0,bestWeight: 0,bmiLevel: ''})
const schema = yup.object({
bodyHeight: yup.number().min(1,'身長は0以上の数値を入力して下さい。').required('身長を入力してください。'),
bodyWeight: yup.number().min(1,'体重は0以上の数値を入力して下さい。').required('体重を入力してください。'),
measurementDate: yup.date().typeError('正しい日付を入力してください').required('計測日を入力してください。').test('measurementDateCheck', '未来の日付を指定することはできません。', (value) => value <= new Date()),
});
console.log(state)
//https://jaredpalmer.com/formik/docs/overview
return (
<Formik
validationSchema={schema}
onSubmit={(values) => {
const resultBmi = (values.bodyWeight/((values.bodyHeight/100)*(values.bodyHeight/100))).toFixed(2)
const bestWeight= ((values.bodyHeight/100)*(values.bodyHeight/100)*22).toFixed(2)
let bmiLevel = '肥満'
if(resultBmi<18.5){
bmiLevel = 'やせ型'
}else if(resultBmi<24.9){
bmiLevel = '普通'
}else if(resultBmi<30){
bmiLevel = '肥満度1'
}else if(resultBmi<35){
bmiLevel = '肥満度2'
}else if(resultBmi<40){
bmiLevel = '肥満度3'
}else if(resultBmi<=40){
bmiLevel = '肥満度4'
}
setBmi({resultBmi,bmiLevel,bestWeight});
dispatch({
type: CREATE_BMI,
measurementDate: values.measurementDate,
bodyHeight: values.bodyHeight,
bodyWeight: values.bodyWeight,
resultBmi,
bmiLevel,
bestWeight
})
}}
initialValues={{ bodyHeight: '', bodyWeight:'', measurementDate: moment().format('YYYY-MM-DD')}}
>
{
({handleSubmit, handleChange, handleBlur, values, errors, touched}) => (
<>
<h4>BMI計算</h4>
<Form noValidate onSubmit={handleSubmit}>
<Form.Group controlId="formGroupMeasurementDate">
<Form.Label>日付</Form.Label>
<Form.Control name="measurementDate" type="date" placeholder="計測日を入力してください" value={values.measurementDate} onBlur={handleBlur} onChange={handleChange} isInvalid={!!(touched.measurementDate && errors.measurementDate)}/>
<Form.Control.Feedback type="invalid">{errors.measurementDate}</Form.Control.Feedback>
</Form.Group>
<Form.Group controlId="formGroupName">
<Form.Label>身長</Form.Label>
<Form.Control name="bodyHeight" type="number" min="0" step="0.01" placeholder="身長を入力して下さい" value={values.bodyHeight} onBlur={handleBlur} onChange={handleChange} isInvalid={!!(touched.bodyHeight && errors.bodyHeight)}/>
<Form.Control.Feedback type="invalid">{errors.bodyHeight}</Form.Control.Feedback>
</Form.Group>
<Form.Group controlId="formGroupWeight">
<Form.Label>体重</Form.Label>
<Form.Control name="bodyWeight" type="number" min="0" step="0.01" placeholder="体重を入力して下さい" value={values.bodyWeight} onBlur={handleBlur} onChange={handleChange} isInvalid={!!(touched.bodyWeight && errors.bodyWeight)}/>
<Form.Control.Feedback type="invalid">{errors.bodyWeight}</Form.Control.Feedback>
</Form.Group>
<div className='text-center'>
<Button variant="primary" type="submit">計算</Button>
</div>
</Form>
<div>
<h4>BMI結果</h4>
BMI: {bmi.resultBmi}<br />
診断: {bmi.bmiLevel}
適正体重: {bmi.bestWeight}
</div>
<div className={state.bmis.length > 0 ? 'active' : 'hidden'}>
<Bmis />
</div>
</>
)}
</Formik>
)
}
export default BmiForm
- components/bmi/bmis.jsファイル
import React,{useContext} from 'react'
import Bmi from './Bmi'
import AppContext from '../../contexts/AppContext'
const Bmis = () => {
const { state } = useContext(AppContext)
return (
<>
<h4>BMI履歴</h4>
<table className="table table-hover">
<thead>
<tr>
<td>日付</td>
<td>体重</td>
<td>BMI</td>
<td>肥満度</td>
<td>ベスト体重</td>
<td>削除</td>
</tr>
</thead>
<tbody>
{ state.bmis.map((bmi, index) => (<Bmi key={index} bmi={bmi} />)) }
</tbody>
</table>
</>
)
}
export default Bmis
import React,{useContext} from 'react'
import { DELETE_BMI } from '../../actions'
import AppContext from '../../contexts/AppContext'
const Bmi = ({bmi}) => {
const {dispatch} = useContext(AppContext)
const measurementDate = bmi.measurementDate
const handleClickDeleteButton = () => {
const result = window.confirm(`${measurementDate}のBMIデータを削除しても良いですか?`)
if (result) {
dispatch({ type: DELETE_BMI,measurementDate })
}
}
return (
<tr>
<td>{bmi.measurementDate}</td>
<td>{bmi.bodyWeight}</td>
<td>{bmi.resultBmi}</td>
<td>{bmi.bmiLevel}</td>
<td>{bmi.bestWeight}</td>
<td>
<button type="button" className="btn btn-danger" onClick={handleClickDeleteButton}>削除</button>
</td>
</tr>
)
}
export default Bmi
import { createContext } from 'react'
const AppContext = createContext()
export default AppContext
export const CREATE_BMI = 'CREATE_BMI'
export const DELETE_BMI = 'DELETE_BMI'
import {
CREATE_BMI,
DELETE_BMI,
} from '../actions'
const bmis = (state = [], action) => {
switch (action.type) {
case CREATE_BMI:
const bmi = {
bodyHeight: action.bodyHeight,
bodyWeight: action.bodyWeight,
measurementDate: action.measurementDate,
resultBmi: action.resultBmi,
bmiLevel: action.bmiLevel,
bestWeight: action.bestWeight
}
const notUpdateBmis = state.filter(bmi => bmi.measurementDate !== action.measurementDate)
const UpdateBmis = [...notUpdateBmis, bmi]
UpdateBmis.sort((a,b) =>a.measurementDate < b.measurementDate ? 1 : -1)
return UpdateBmis
case DELETE_BMI:
return state.filter(bmi => bmi.measurementDate !== action.measurementDate)
default:
return state
}
}
export default bmis
import {combineReducers} from 'redux'
import bmis from './bmis'
export default combineReducers({bmis})
- components/App.js (下記の内容だけでは他の情報もあるため、超ざっくりです。)
import React, { useState, useEffect, useReducer } from 'react'
import { Nav, Container, Form, Button, Table, Navbar } from 'react-bootstrap'
import axios from 'axios'
import AppContext from '../contexts/AppContext'
import reducer from '../reducers'
const [state, dispatch] = useReducer(reducer, initialBmiState)
return (
<AppContext.Provider value={{ state, dispatch }}>
<Router>
(省略)
</Router >
</AppContext.Provider>
作成した画面