[MERN①] Express & MongoDB Setup
https://qiita.com/niyomong/private/3281af84486876f897f7
[MERN②]User API Routes & JWT Authentication
https://qiita.com/niyomong/private/c11616ff7b64925f9a2b
[MERN③] Profile API Routes
https://qiita.com/niyomong/private/8cff4e6fa0e81b92cb49
[MERN④] Post API
https://qiita.com/niyomong/private/3ce66f15375ad04b8989
[MERN⑤] Getting Started With React & The Frontend
https://qiita.com/niyomong/private/a5759e2fb89c9f222b6b
[MERN⑥] Redux Setup & Alerts
https://qiita.com/niyomong/private/074c27259924c7fd306b
[MERN⑦] React User Authentication
https://qiita.com/niyomong/private/37151784671eff3b92b6
[MERN⑧] Dashboard & Profile Management
https://qiita.com/niyomong/private/ab7e5da1b1983a226aca
[MERN⑨] Profile Display
https://qiita.com/niyomong/private/42426135e959c7844dcb
[MERN⑩] Posts & Comments
https://qiita.com/niyomong/private/19c78aea482b734c3cf5
[MERN11] デプロイ
https://qiita.com/niyomong/private/150f9000ce51548134ad
1. React & Concurrently Setup
1.1 reactフォルダを作成 (例)フォルダ名-> client
$ npx create-react-app client
1.2 package.jsonに同時起動するように下記追加。
{
...
"scripts": {
"start": "node server",
"server": "nodemon server",
+ "client": "npm start --prefix client",
+ "dev": "concurrently \"npm run server\" \"npm run client\""
},
...
"devDependencies": {
"concurrently": "^5.3.0",
"nodemon": "^2.0.4"
}
}
・「---prefix client」:clientフォルダ内のファイルっていう意味
・concurrently "起動したいサーバー①" "起動したいサーバー②""
1.3 ExpressとReactを両方同時に起動する。
$ npm run dev
1.4 ライブラリ(Dependencies)をインストール
$ npm i axios react-router-dom redux react-redux redux-thunk redux-devtools-extension moment react-moment
~~ 補足説明 ~~
省略。
1.5 axiosで毎回「http://xxx」の記述を省略したい
-> axios.get(http://xxx/api/xx
)ではなく、axios.get(/api/xx
)に省略する
...
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
+ "proxy": "http://localhost:5000"
}
2. Clean Up & Initial Components
2.1 (create-react-appでデフォルトで生成される)不要なファイルを削除
$ rm .gitignore README.md
$ rm -rf .git
$ rm src/App.test.js src/index.css src/logo.svg src/serviceWorker.js src/setupTests.js
-> index.jsとApp.jsで上記で削除したファイルのimport等を削除する
-> App.cssファイルの中身すべて削除
-> App.jsの中身を下記の通り整理する。
【ここ確認!】「const App = () =>」はHooks使ってる?
import React, { Fragment } from 'react';
import './App.css';
const App = () => (
<Fragment>
<h1>App</h1>
</Fragment>
);
export default App;
2.2 Fontawesomeを設置
<!DOCTYPE html>
<html lang="en">
<head>
...
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
+ <script src="https://kit.fontawesome.com/ce63691a5a.js" crossorigin="anonymous"></script>
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<title>XXXXX</title>
</head>
2.3 layout関連ファイル作成
mkdir src/img
mkdir src/components
mkdir src/components/layout
touch src/components/layout/Navbar.js
touch src/components/layout/Landing.js
① racfe -> 「Enter」で以下記述される。
import React from 'react'
export const Navbar = () => {
return (
<div></div>
)
}
② 上記のNavbar.jsにNavbarのテーマを
のところに挿入。 <nav class="navbar bg-dark">
<h1>
<a href="index.html"><i class="fas fa-code"></i> DevConnector</a>
</h1>
<ul>
<li><a href="profiles.html">Developers</a></li>
<li><a href="register.html">Register</a></li>
<li><a href="login.html">Login</a></li>
</ul>
</nav>
③ class -> classNameに変える(React仕様)
import React from 'react';
const Navbar = () => {
return (
<nav className="navbar bg-dark">
<h1>
<a href="index.html"><i className="fas fa-code"></i> サービス名</a>
</h1>
<ul>
<li><a href="profiles.html">Developers</a></li>
<li><a href="register.html">Register</a></li>
<li><a href="login.html">Login</a></li>
</ul>
</nav>
);
};
export default Navbar;
④ Landing.jsも同じようにLandingテーマを
⑤ App.jsにNavbarとLandingを設置。
import React, { Fragment } from 'react';
+ import Navbar from './components/layout/Navbar';
+ import Landing from './components/layout/Landing';
import './App.css';
const App = () => (
<Fragment>
+ <Navbar />
+ <Landing />
</Fragment>
);
export default App;
3. React Router Setup
①src/components/authフォルダにLogin.jsとRegister.jsを生成
-> 「racfe」
② React Routerを設置
import React, { Fragment } from 'react';
+ import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import Navbar from './components/layout/Navbar';
import Landing from './components/layout/Landing';
+ import Register from './components/auth/Register';
+ import Login from './components/auth/Login';
import './App.css';
const App = () => (
+ <Router>
<Fragment>
<Navbar />
+ <Route exact path="/" component={Landing} />
+ <section className="container">
+ <Switch>
+ <Route exact path="/register" component={Register} />
+ <Route exact path="/login" component={Login} />
+ </Switch>
+ </section>
</Fragment>
+ </Router>
);
export default App;
//① import { Link } from 'react-router-dom';
//② <a href="!#">xxx</a> を <Link to="/xxx"></Link> に変える。
import React from 'react';
+ import { Link } from 'react-router-dom';
const Navbar = () => {
return (
<nav className="navbar bg-dark">
<h1>
+ <Link to="/"><i className="fas fa-code"></i> RefNote</Link>
</h1>
<ul>
<li><a href="#!">Developers</a></li>
+ <li><Link to="/register">Register</Link></li>
+ <li><Link to="/login">Login</Link></li>
</ul>
</nav>
);
};
export default Navbar;
4. Register 「Form」 & useState Hook
4.1 HTML記述をで囲む
4.2 入力蘭それぞれ「state」と「changeハンドラー」を持たせる。 ->「state」をUpdateするため。
4.2A「useState(Hooks)」を使う!!!
////通常の書き方////
//①formDataに該当する書き方
state = {
formData: {
name: '', email: '', password: '', password2: '',
},
};
//②setFormDataに該当する書き方
this.setState
↓↓↓↓↓↓
////Hooks////
const [①formData, ②setFormData] = useState({
name: '', email: '', password: '', password2: '',
});
//【説明】
//①formData(オブジェクト)②setFormData(関数)をuseState({オブジェクトs})に格納する。
4.2B onChangeハンドラーの分離(要はreturn後で直接記述せずreturnの前で定義する)
const onChange = (e) => ②setFormData({ ③...formData, ④[e.target.name]: e.target.value });
//③ {...オブジェクト名} は「Spread Attributes」と呼ばれる。
//指定したオブジェクト名でオブジェクトのプロパティをまとめて下の階層に渡せる。
//オブジェクトのプロパティを追加したり削除したりするときに、コンポーネントのオブジェクトをいじる必要がなくなる。
//④「nameをkeyとして」データを渡しているが、[e.target.name]だといかなるname keyでも対応できる。
(参考)Spread Attributes「...」 https://mae.chab.in/archives/2897
import React, { Fragment, useState } from 'react';
const Register = () => {
const [formData, setFormData] = useState({
name: '',
email: '',
password: '',
password2: '',
});
const { name, email, password, password2 } = formData;
const onChange = (e) =>
setFormData({ ...formData, [e.target.name]: e.target.value });
const onSubmit = (e) => {
e.preventDefault();
if (password !== password2) {
console.log('Passwords do not match');
} else {
console.log('SUCCESS');
}
};
return (
<Fragment>
<h1 className="large text-primary">Sign Up</h1>
<p className="lead">
<i className="fas fa -user"></i> Create Your Account
</p>
+ <form className="form" onSubmit={(e) => onSubmit(e)}>
<div className="form-group">
<input
type="text"
placeholder="Name"
name="name"
+ value={name}
+ onChange={(e) => onChange(e)}
required
/>
</div>
<div className="form-group">
<input
type="email"
placeholder="Email Address"
name="email"
value={email}
onChange={(e) => onChange(e)}
required
/>
<small className="form-text">
This site uses Gravatar so if you want a profile image, use a
Gravatar email
</small>
</div>
<div className="form-group">
<input
type="password"
placeholder="Password"
name="password"
value={password}
onChange={(e) => onChange(e)}
minLength="6"
/>
</div>
<div className="form-group">
<input
type="password"
placeholder="Confirm Password"
name="password2"
value={password2}
onChange={(e) => onChange(e)}
minLength="6"
/>
</div>
<input type="submit" className="btn btn-primary" value="Register" />
</form>
<p className="my-1">
Already have an account? <a href="login.html">Sign In</a>
</p>
</Fragment>
);
};
export default Register;
4. Login Form
-> Register.jsを同じ。
(不要なInputsは削除。例えばname等)