引き続きダッシュボードへ情報の追加・編集ができるようコーディングしました。今回は経験(experiences)と学歴・教育(school, degree etc.)です。(このWebアプリケーションは開発者用ですので、教育欄にはプログラミングのbootcampsも含みます。)
①client/src/actions/types.js にUPDATE_PROFILEを追加
パターン化して覚えてきました。まずは、types.jsにUPDATE_PROFILEを追加します。
export const UPDATE_PROFILE = "UPDATE_PROFILE";
②client/src/actions/profile.jsにもUPDATE_PROFILEを追加する
先程のtype.jsからUPDATE_PROFILEをインポートします。
import { GET_PROFILE, PROFILE_ERROR, UPDATE_PROFILE } from "./types";
前回と同じパターンでExperiencesとEducationが追加できるようコードを書いていきます。両方共ほぼ同じコードです。
// ADD Experience export const addExperience = (formData, history) => async dispatch => { try { const config = { headers: { "Content-Type": "application/json" } }; const res = await axios.put("/api/profile/experience", formData, config); dispatch({ type: UPDATE_PROFILE, payload: res.data }); dispatch(setAlert("Experience Added", "success")); history.push("/dashboard"); } catch (err) { const errors = err.response.data.errors; if (errors) { errors.forEach(error => dispatch(setAlert(error.msg, "danger"))); } dispatch({ type: PROFILE_ERROR, payload: { msg: err.response.statusText, status: err.response.status } }); } }; // ADD Education export const addEducation = (formData, history) => async dispatch => { try { const config = { headers: { "Content-Type": "application/json" } }; const res = await axios.put("/api/profile/education", formData, config); dispatch({ type: UPDATE_PROFILE, payload: res.data }); dispatch(setAlert("Education Added", "success")); history.push("/dashboard"); } catch (err) { const errors = err.response.data.errors; if (errors) { errors.forEach(error => dispatch(setAlert(error.msg, "danger"))); } dispatch({ type: PROFILE_ERROR, payload: { msg: err.response.statusText, status: err.response.status } }); } };
③client/src/reducers/profile.js レデューサーにもUPDATE_PROFILEの情報を書き足す
まずは、import部分です。
import { GET_PROFILE, PROFILE_ERROR, CLEAR_PROFILE, UPDATE_PROFILE } from "../actions/types";
次に、function のswitch内の caseにも同じように、UPDATE_PROFILE情報を書いていきます。今回ここに書いたのは、case UPDATE_PROFILE: 一行のみです。
export default function(state = initialState, action) { const { type, payload } = action; switch (type) { case GET_PROFILE: case UPDATE_PROFILE: return { ...state, profile: payload, loading: false }; case PROFILE_ERROR: return { ...state, error: payload, loading: false }; case CLEAR_PROFILE: return { ...state, profile: null, repos: [], loading: false }; default: return state; } }
④新しいファイルを2つ作成 client/src/components/profile-form/AddExperience.jsそしてAddEducation.jsを新規作成して、記入部分を作る
AddExperience.jsに関しては、現在も今の会社に努めている場合、to(何年何月まで在籍)の部分を使わなくて良いように設定しました。
const [toDateDisabled, toggleDisabled] = useState(false);や、input name='current'のあたりのコードです。
import React, { Fragment, useState } from "react"; import { Link, withRouter } from "react-router-dom"; import PropTypes from "prop-types"; import { connect } from "react-redux"; import { addExperience } from "../../actions/profile"; const AddExperience = ({ addExperience, history }) => { const [formData, setFormData] = useState({ company: "", title: "", location: "", from: "", to: "", current: false, description: "" }); const [toDateDisabled, toggleDisabled] = useState(false); const { company, title, location, from, to, current, description } = formData; const onChange = e => setFormData({ ...formData, [e.target.name]: e.target.value }); return ( <Fragment> <h1 className='large text-primary'>Add An Experience</h1> <p className='lead'> <i className='fas fa-code-branch' /> Add any developer/programming positions that you have had in the past </p> <small>* = required field</small> <form className='form' onSubmit={e => { e.preventDefault(); addExperience(formData, history); }} > <div className='form-group'> <input type='text' placeholder='* Job Title' name='title' value={title} onChange={e => onChange(e)} required /> </div> <div className='form-group'> <input type='text' placeholder='* Company' name='company' value={company} onChange={e => onChange(e)} required /> </div> <div className='form-group'> <input type='text' placeholder='Location' name='location' value={location} onChange={e => onChange(e)} /> </div> <div className='form-group'> <h4>From Date</h4> <input type='date' name='from' checked={current} value={from} onChange={e => onChange(e)} /> </div> <div className='form-group'> <p> <input type='checkbox' name='current' value={current} onChange={e => { setFormData({ ...formData, current: !current }); toggleDisabled(!toDateDisabled); }} />{" "} Current Job </p> </div> <div className='form-group'> <h4>To Date</h4> <input type='date' name='to' value={to} onChange={e => onChange(e)} disabled={toDateDisabled ? "disabeld" : ""} /> </div> <div className='form-group'> <textarea name='description' cols='30' rows='5' placeholder='Job Description' value={description} onChange={e => onChange(e)} /> </div> <input type='submit' className='btn btn-primary my-1' /> <Link className='btn btn-light my-1' to='dashboard.html'> Go Back </Link> </form> </Fragment> ); }; AddExperience.propTypes = { addExperience: PropTypes.func.isRequired }; export default connect( null, { addExperience } )(withRouter(AddExperience));
AddEducation.jsは、上記のコードを全てコピペした上で、ExperienceをEducationに変更、companyなどの部分をschool, degree, fieldofstudyに書き換えるなどのマイナーチェンジを行いました。
⑤最後はApp.jsに今日のコーディングの情報を書き加えて、MongoDB Atlasでデータの追加・編集ができることを確認して本日の学習終了
importの部分に今日作成したAddExperienceやAddEducationの情報、そしてexperienceとeducationのPrivate Routeを書き加えました。最後に実際にlocalhostのダッシュボード画面で、ユーザーのexperiencesとeducationの情報を入力して、データがMongo DB Atlasで反映されていることを確認して本日の学習は完了となりました。
import React, { Fragment, useEffect } 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 Alert from "./components/layout/Alert"; import Dashboard from "./components/dashboard/Dashboard"; import CreateProfile from "./components/profile-form/CreateProfile"; import EditProfile from "./components/profile-form/EditProfile"; import AddExperience from "./components/profile-form/AddExperience"; import AddEducation from "./components/profile-form/AddEducation"; import PrivateRoute from "./components/routing/PrivateRoute"; //Redux import { Provider } from "react-redux"; import store from "./store"; import { loadUser } from "./actions/auth"; import setAuthToken from "./utils/setAuthToken"; import "./App.css"; if (localStorage.token) { setAuthToken(localStorage.token); } const App = () => { useEffect(() => { store.dispatch(loadUser()); }, []); return ( <Provider store={store}> <Router> <Fragment> <Navbar /> <Route exact path='/' component={Landing} /> <section className='container'> <Alert /> <Switch> <Route exact path='/register' component={Register} /> <Route exact path='/login' component={Login} /> <PrivateRoute exact path='/dashboard' component={Dashboard} /> <PrivateRoute exact path='/create-profile' component={CreateProfile} /> <PrivateRoute exact path='/edit-profile' component={EditProfile} /> <PrivateRoute exact path='/add-experience' component={AddExperience} /> <PrivateRoute exact path='/add-education' component={AddEducation} /> </Switch> </section> </Fragment> </Router> </Provider> ); }; export default App;
できれば次のレクチャーまで行きたかったのですが、マイナーなdebugを繰り返しているうちに3時間経過しましたので、本日はここまでです。次回は複数レクチャー進めることが目標です。
現在使用している教材と学習時間:本日の学習時間3.0時間
Udemy:MERN Stack Front To Back: Full Stack React, Redux & Node.js by Brad Traversy
★Section9:Dashboard & Profile Management
-Lec 50 Add Education & Experiences (このブログ記事に書いた内容。本日はここまで完了)
進捗状況:70%
学習時間3.0時間
~本日は休みにしている教材~
Udemy:The Complete Web Developer: Zero to Mastery by Andrei Neagoie
進捗状況: 92%
★参考にした本★
「React.js & Next.js超入門」掌田津耶乃 (秀和システム)
Section4-1: Reduxを使ってみよう
★参照記事★
Quiita参照記事⇒react-routerのページ遷移をhandleで行う時にはwithRouterを使う