ダッシュボードが形になってきました。ユーザーが学歴・教育や仕事経験を記入・削除できるようにして、Webアプリケーションからのアカウント削除もできるようにコーディングしました。本日は、昨日目標にしていた通り、Udemyのレクチャーを2つ完了させることができました!
①client/src/components/dashboard/Education.js 新ファイル作成で学歴や教育を記入・削除できるようにする. React でmoment & Momentを使用して日時表示をわかりやすくする
今回初めて、Momentとmomentをimportしたのですが、import Moment from 'react-moment'; の方は大文字で、import moment from 'moment';の方は小文字なのでそこは気をつけなければと感じました。Reactは便利な機能が沢山あって、全ては覚えきれないかもしれませんが、新しいやり方はどんどん取り入れていきたいです。
momentの使い方についてわかりやすい記事が公開されていました。例として、いくつものコード例があります。
⇒Using Moment JS in React Native (Sudhir Kumar in Better Programming)
import React, { Fragment } from "react"; import PropTypes from "prop-types"; import Moment from "react-moment"; import moment from "moment"; import { connect } from "react-redux"; import { deleteEducation } from "../../actions/profile"; const Education = ({ education, deleteEducation }) => { const educations = education.map(edu => ( <tr key={edu._id}> <td>{edu.school}</td> <td className='hide-sm'>{edu.degree}</td> <td> <Moment format='YYYY/MM/DD'>{moment.utc(edu.from)}</Moment> -{" "} {edu.to === null ? ( " Now" ) : ( <Moment format='YYYY/MM/DD'>{moment.utc(edu.to)}</Moment> )} </td> <td> <button onClick={() => deleteEducation(edu._id)} className='btn btn-danger' > Delete </button> </td> </tr> )); return ( <Fragment> <h2 className='my-2'>Education Credentials</h2> <table className='table'> <thead> <tr> <th>School</th> <th className='hide-sm'>Degree</th> <th className='hide-sm'>Years</th> <th /> </tr> </thead> <tbody>{educations}</tbody> </table> </Fragment> ); }; Education.propTypes = { education: PropTypes.array.isRequired, deleteEducation: PropTypes.func.isRequired }; export default connect( null, { deleteEducation } )(Education);
②client/src/components/dashboard/Experience.js 新ファイル作成。仕事の経験についてもリスト形式で記入できるようにする
Education.jsをコピペしてきて、必要部分のみ書き換えました。様式はほぼ同じです。
import React, { Fragment } from "react"; import PropTypes from "prop-types"; import Moment from "react-moment"; import { connect } from "react-redux"; import { deleteExperience } from "../../actions/profile"; const Experience = ({ experience, deleteExperience }) => { const experiences = experience.map(exp => ( <tr key={exp._id}> <td>{exp.company}</td> <td className='hide-sm'>{exp.title}</td> <td> <Moment format='YYYY/MM/DD'>{exp.from}</Moment> -{" "} {exp.to === null ? ( " Now" ) : ( <Moment format='YYYY/MM/DD'>{exp.to}</Moment> )} </td> <td> <button onClick={() => deleteExperience(exp._id)} className='btn btn-danger' > Delete </button> </td> </tr> )); return ( <Fragment> <h2 className='my-2'>Experience Credentials</h2> <table className='table'> <thread> <tr> <th>Company</th> <th className='hide-sm'>Title</th> <th className='hide-sm'>Years</th> <th /> </tr> </thread> <tbody>{experiences}</tbody> </table> </Fragment> ); }; Experience.propTypes = { experience: PropTypes.array.isRequired, deleteExperience: PropTypes.func.isRequired }; export default connect( null, { deleteExperience } )(Experience);
③client/src/components/dashboard/Dashboard.jsに学歴・教育・経験・アカウント削除についての情報を書き加える
元々あったDashboard.jsにもアカウント削除についての情報を書き加えました。具体的には、deleteAccountの部分、Fragment内のExperienceとEducationの部分、アカウント削除のボタン作成、最後の部分のpropTypesやconnectの書き換えです。
import React, { Fragment, useEffect } from "react"; import { Link } from "react-router-dom"; import PropTypes from "prop-types"; import { connect } from "react-redux"; import Spinner from "../layout/Spinner"; import DashboardActions from "./DashboardActions"; import Experience from "./Experience"; import Education from "./Education"; import { getCurrentProfile, deleteAccount } from "../../actions/profile"; const Dashboard = ({ getCurrentProfile, deleteAccount, auth: { user }, profile: { profile, loading } }) => { // eslint-disable-next-line useEffect(() => { getCurrentProfile(); }, []); return loading && profile === null ? ( <Spinner /> ) : ( <Fragment> <h1 className='large text-primary'>Dashboard</h1> <p className='lead'> <i className='fas fa-user' /> Welcome {user && user.name} </p> {profile !== null ? ( <Fragment> <DashboardActions /> <Experience experience={profile.experience} /> <Education education={profile.education} /> <div className='my-2'> <button className='btn btn-danger' onClick={() => deleteAccount()}> <i className='fas fa-user-minnus' /> Delete My Account </button> </div> </Fragment> ) : ( <Fragment> <p>You have not yet setup a profile, please add some info</p> <Link to='/create-profile' className='btn btn-primary my-1'> Create Profile </Link> </Fragment> )} </Fragment> ); }; Dashboard.propTypes = { getCurrentProfile: PropTypes.func.isRequired, deleteAccount: PropTypes.func.isRequired, auth: PropTypes.object.isRequired, profile: PropTypes.object.isRequired }; const mapStateToProps = state => ({ auth: state.auth, profile: state.profile }); export default connect( mapStateToProps, { getCurrentProfile, deleteAccount } )(Dashboard);
④client/src/actions/profile.jsに学歴・教育・アカウントの削除について書き加える
まずは、import部分から。
import { GET_PROFILE, PROFILE_ERROR, UPDATE_PROFILE, CLEAR_PROFILE, ACCOUNT_DELETED } from "./types";
そして、学歴・教育・アカウントの削除についてのコーディングです。これまで書いてきたコード(学歴の追加など)とほぼ同じコードの書き方でした。情報の追加ではaxios.putとしていたところを、情報の削除をするためにaxios.deleteにしました。
また、アカウント削除については、一度消すと元に戻れないのでwindow.confirm()を使って一度アラートを出してから、アカウント削除に進む手順になっています。
//Delete experience export const deleteExperience = id => async dispatch => { try { const res = await axios.delete(`/api/profile/experience/${id}`); dispatch({ type: UPDATE_PROFILE, payload: res.data }); dispatch(setAlert("Experience Removed", "success")); } catch (err) { dispatch({ type: PROFILE_ERROR, payload: { msg: err.response.statusText, status: err.response.status } }); } }; //Delete education export const deleteEducation = id => async dispatch => { try { const res = await axios.delete(`/api/profile/education/${id}`); dispatch({ type: UPDATE_PROFILE, payload: res.data }); dispatch(setAlert("Education Removed", "success")); } catch (err) { dispatch({ type: PROFILE_ERROR, payload: { msg: err.response.statusText, status: err.response.status } }); } }; //Delete account export const deleteAccount = () => async dispatch => { if (window.confirm("Are you sure? This can NOT be undone!")) { try { const res = await axios.delete(`/api/profile/`); dispatch({ type: CLEAR_PROFILE }); dispatch({ type: ACCOUNT_DELETED }); dispatch(setAlert("Your account has been permanently deleted")); } catch (err) { dispatch({ type: PROFILE_ERROR, payload: { msg: err.response.statusText, status: err.response.status } }); } } };
⑤client/src/actions/types.jsにアカウント削除について書き加える。
types.jsに書き加えたのは一行のみで、ACCOUNT_DELETEDの情報のみです。
export const ACCOUNT_DELETED = "ACCOUNT_DELETED";
⑥client/src.reducers/auth.jsにアカウント削除について書き加える。
まずは、import部分。そして、switch内のcase ACCOUNT_DELETEDという部分です。
import { REGISTER_SUCCESS, REGISTER_FAIL, USER_LOADED, AUTH_ERROR, LOGIN_SUCCESS, LOGIN_FAIL, LOGOUT, ACCOUNT_DELETED } from "../actions/types";
switch内のcase ACCOUNT_DELETED。それぞれ一行のみの書き加えです。
export default function(state = initialState, action) { const { type, payload } = action; switch (type) { case USER_LOADED: return { ...state, isAuthenticated: true, loading: false, user: payload }; case REGISTER_SUCCESS: case LOGIN_SUCCESS: localStorage.setItem("token", payload.token); return { ...state, ...payload, isAuthenticated: true, loading: false }; case REGISTER_FAIL: case AUTH_ERROR: case LOGIN_FAIL: case LOGOUT: case ACCOUNT_DELETED: localStorage.removeItem("token"); return { ...state, token: null, isAuthenticated: false, loading: false }; default: return state; } }
⑦routes/api/profile.jsで、ユーザーがポストしたコメントを削除できるようコーディング
ユーザーがポストしたコメントも削除できるように、一部書き加えました。
const Post = require("../../models/Post");
下記コードの中ではawait Post.deleteMany()の部分です。
// @route DELETE api/profile // @desc Delete profile, user & posts // @access Private router.delete("/", auth, async (req, res) => { try { // Remove user posts await Post.deleteMany({ user: req.user.id }); // Remove profile await Profile.findOneAndRemove({ user: req.user.id }); // Remove user await User.findOneAndRemove({ _id: req.user.id }); res.json({ msg: "User deleted" }); } catch (err) { console.error(err.message); res.status(500).send("Server Error"); } });
MongoDBでのdeleteMany()の使い方について(collection内のドキュメント削除)
今回deleteMany()を初めて使用したので、使い方を調べてみたところ、MongoDBでコレクションからドキュメントを削除する時に使用するとmongoDB公式サイトに掲載されていました。
⇒db.collection.deleteMany (MongoDB Reference)
現在使用している教材と学習時間:本日の学習時間2.5時間
Udemy:MERN Stack Front To Back: Full Stack React, Redux & Node.js by Brad Traversy
★Section9:Dashboard & Profile Management
-Lec 51 List Education & Experiences
-Lec52 Delete Education, Experiences & Account(このブログ記事に書いた内容。本日はここまで完了。Section9が完了しました!)
進捗状況:72%
学習時間2.5時間
~本日は休みにしている教材~
Udemy:The Complete Web Developer: Zero to Mastery by Andrei Neagoie
進捗状況: 92%
★参考にした本★
「React.js & Next.js超入門」掌田津耶乃 (秀和システム)
Section4-1: Reduxを使ってみよう
★参照記事★
Quiita参照記事⇒react-routerのページ遷移をhandleで行う時にはwithRouterを使う