MERNことはじめ
背景
MERN 開発の流れを知りたくてyotuube(https://www.youtube.com/watch?v=7CqJlxBYj-M)を見た。
動画は情報量が多くて捗るけど、その分繰り返し見るには時間がかかるので文字に起こした。
MongoDBの説明
- Database
- Collection
- Document
- Index
- $lookup
- Reference
Example
Mongo DBの例
{ name:" ", title:" ", address:{ address1:" ", city:" ", state: " ", }, topics:[" ", " ", " ",], number: }
MogoDB Atlasの紹介
512MBまで無料、Cluster使えるとか
作成するアプリの説明
Appは以下を含む
- Exercises
- Users
全てのエクササイズは一人のユーザーのみ持つ
ディレクトリの作成と環境の整備
特定のディレクトリで、
create-react-app mern-exercise-tracker
数分待って完了したらディレクトリ変更
cd mern-exercise-tracker
バックエンド用のディレクトリを作成し環境もろもろを整備する
mkdir backend cd backend npm install express cors mongoose dotenv npm install -g nodemon
バックエンドとMongoDBの接続
backendディレクトリにserver.jsファイルを新規作成する。
const express = require('express'); const cors = require('cors'); const mongoose = require('mongoose'); require('dotenv').config(); const app = express(); const port = process.env.PORT || 5000; app.use(cors()); app.use(express.json()); const uri = process.env.ATLAS_URI; mongoose.connect(uri, {userNewUrlParser: true, userCreateIndex: true }); const connection = mongoose.connection; connection.once('open', () => { console.log("MongoDB database connection established successfully"); }) const exercisesRouter = require('./routes/exercises'); const usersRouter = require('./routes/users'); app.use('/exercises', exercisesRouter); app.use('/users', usersRouter); app.listen(port,() => { console.log(`Server is runnning on port: ${port}`); });
backendディレクトリに.envファイルを新規作成する。
MongoDB Atlas clusterのconnection clusterのconnection String OnlyのCopyをクリック
.envに以下を記載。
ATLAS_URI=ペースト(先ほどコピーしたもの)
passwordの箇所にパスワードを記載する。
backendディレクトリでnodemon server
以上でMongoDBにアクセスできるようになる。
modelの作成
backendにmodelsフォルダを作成。
modelsフォルダに
- exercise.model.js
- user.model.js
を新規作成する。
exercise.model.js
const mongoose = require('monngoose'); const Schema = mongoose.Schema; const exerciseSchema = new Schema({ username: { type: String, required: true}, description: { type: String, required: true}, duration: { type: Number, required: true}, date: { type: Date, required: true}, }, { timestamps: true, }); const Exercise = mongoose.model('Exercise', exerciseSchema); module.exports = Exercise;
user.mode.js
const mongoose = require('monngoose'); const Schema = mongoose.Schema; const userSchema = new Schema({ username:{ type: String, required: true, unique: true, trim: true, minlength: 3 }, }, { timestamps: true, }); const User = mongoose.model('User', userSchema); module.exports = User;
Routeの作成
backendディレクトリ内にroutesディレクトリを作成する。
routesディレクトリ内に
- exercises.js
- users.js
を新規作成する。
exercises.js
const router = require('express').Router(); let Exercise = require('../models/exrecise.model'); router.route('/').get((req, res) => { Exercise.find() .then(exercises => res.json(exercises)) .catch(err => res.status(400).json('Error: ' + err)); }); router.route('/add').post((req, res) => { const username = req.body.username; const description = req.body.description; const duration = Number(req.body.duration); const date = Date.parse(req.body.date); const newExercise = new Exercise({ username, description, duration, date, }); newExercise.save() .then(() => res.json('Exercise added!')) .catch(err => res.status(400).json('Error: ' + err)); }); module.exports = router;
user.js
const router = require('express').Router(); let User = require('../models/user.model'); router.route('/').get((req, res) => { User.find() .then(users => res.json(users)) .catch(err => res.status(400).json('Error: ' + err)); }); router.route('/add').post((req, res) => { const username = req.body.username; const newUser = new User({username}); newUser.save() .then(() => res.json('User added!')) .catch(err => res.status(400).json('Error: ' + err)); }); module.exports = router;
server.jsにルートを記載する(上述)。
---ここまで動画開始から30分---
データベースの確認
Insomniaでサーバーの動作を確認する。
exercises.jsにIDのルートの処理を追加する(上述)。
get,delete,postなど
---ここまで動画開始から43分。ここからReactの説明開始---
ReactでHello Worldの表示
publicのindex.htmlを確認する
index.jsを確認する
App.jsに移動。
exercise-trackerフォルダで
- npm install bootstrap
- npm install reat-router-dom
使用するコンポーネントをあらかじめimportする。
srcディレクトリ下にcomponentディレクトリを作成する。
その後
これ以降のReactの説明に関する情報は少し古い。
Hooksを使用するのが今後のトレンドになるはず。
従ってこれ以降は動画をまとめる必要はないような気がするので
代わりにReact Hooksについて読みかじったことを記載しておく。
参考文献
りあクト!
りあクト! TypeScriptで始めるつらくないReact開発 第2版 - くるみ割り書房 - BOOTH
react training公式ページ
React Training: React Router v5.1
Qiitaの記事
useStateの使い方
import React, {FC, useState } from 'react'; import { Button, Card, Statistic } from 'semantic-ui-react'; import './App.css'; const App:FC = () => { const [count, setCount] = usetState(0); const increment = () => { setCount(count + 1); }; const decremet = () => { setCount(count -1); }; return( <div className="container"> <header> <h1>カウンター</h1> </header> <Card> <Statistic className="number-board"> <Statistic.Label>count</Statistic.Label> <Statistic.value>{count}</Statistic.value> </Statistic> <Card.Content> <div className="ui two buttons"> <Button color="red" onClick={decrement}>-1</Button> <Button color="green" onClick={increment}>+1</Button> </div> </Card.Content> </Card> </div> ); }; export default App;
useEffectの使い方
import React, {FC, useEffect, useState} from 'react'; import { Button, Card, Icon, Statistics } from 'semantic-ui-react'; import './App.css'; const LIMIT = 60; const App:FC = () => { const [timeLeft, setTimeLeft] = useState(LIMIT); const reset = () => { setTimeLeft(LIMIT); }; const tick = () => { setTimeLeft(prevTime => (prevTime === 0? LIMIT: prevTime -1)); }; useEffect(() => { const timerId = setInterval(tick, 1000); return () => clearInterval(timerId); },[]); return ( <div className="container"> <header> <h1>タイマー</h1> </header> <Card> <Statistic className="number-board"> <Statistics.Label>time</Statistics.Label> <Statistics.value>{timeLeft}</Statistics.value> </Statistic> <Card.content> <Button color="red" fluid on Click={reset}> <Icon name="redo" /> Reset </Button> </Card.content> </Card> </div> ); }; export default App;
Custom Hooksの例
import React, { FC, useEffect, useState } from 'react'; import AppComponent from ' ../components/App'; //Custom hook "useTimer" const useTimer = (limitSec: number):[number, () => void] => { const [timeLeft, setTimeLeft] = useState(limitSec); const reset = () => { setTimeLeft(limitSec); }; const tick = () => { setTimeLeft(prev => ( prevTime === 0? limitSec : prevTime -1)); }; useEffect(() => { const timerId = setInterval(tick, 1000); return () => clearInterval(timerId); },[]); return [timeLeft, reset]; } const AppContainer: FC= () => { const LIMIT = 60; const [timeLeft, reset] = useTimer(LIMIT); return <AppComponent timeLeft={timeLeft} reset={reset} />; }; export default AppContainer;
react-routerの例
import React from 'react'; import ReactDom from 'react-dom'; import { Browsewrrouter as router, Route, Switch, useParams, useHistory, useLocation, } from 'react-router-dom'; function Hello(){ const history = useHistory(); return( <div> <h1>Hello</h1> <button onClick={() => history.push('/hello/react-router?message=hooks#test')}>Next</button> </div> ); } function HelloSomeone(){ const history = useHistory(); const location = useLocation(); const {name} = useParams(); return( <div> <h1>Hello {name}</h1> <p>pathname: {location.pathname}</p> <p>search: {location.search} </p> <p>hash: {location.hash}</p> <button onClick={() => history.goBack()}>Go Back</button> </div> ); } function App(){ return ( <Router> <Switch> <Route path="/" exact> <Hello /> </Route> <Route path="/hello/:name" exact> <HelloSomeone/> </Route> </Switch> </Router> ); }