104日目:ダッシュボードの学歴・教育(education)と経験(experiences)を記入&削除・アカウント削除できるように設定する: List Education & Experieces, Delete Education, Experiences & Account (from Dashboard)

ダッシュボードが形になってきました。ユーザーが学歴・教育や仕事経験を記入・削除できるようにして、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を使う

関連キーワード
  • 131日目~134日目:Udemyで一番人気のGit (&GitHub) コースを修了!学習した内容・学習にかかった時間とおすすめ度をご紹介: Completed Git course by Udemy "Git Complete: The definitive, step-by-step guide to Git" by Jason Taylor: Highly recommended to both beginner & intermediate leaner
  • 121日目~130日目:Udemyで新しいReactコース学習とGit & GitHubのコースを受講し始める #100dayofcode Round 2
  • 120日目:プログラミング学習100日チャレンジの完了とこれからの学習&ブログ記事:100days of code completed & from now on
  • 119日目:完成したWebアプリケーションの公開(Devconnector deployed on Heroku)
  • 118日目:Udemy講座の感想口コミ&自分に合った講座の選び方ポイント:Mern Stack Front to Back: Full Stack React, Redux & Node.js by Brad Traversy
  • 117日目:完成!Herokuへのデプロイ成功:Heroku CLIのインストールからWebアプリデプロイまで。Herokuの使い方と、package.jsonとgitコマンドではまったところと解決方法
おすすめの記事
102日目①:Create Profile Action:React routerでwithRouterを使ってリダイレクトを可能にする
プログラミング100日チャレンジ記録 #100daysofcode
今回は、ダッシュボードのプロフィール部分のactionを作成しました。新しく学んだことは、import { withRouter} from...
101日目:Dashboard作成とCreateProfile Componentの作成
プログラミング100日チャレンジ記録 #100daysofcode
Dashboard(ユーザーがログインした時に表示される画面)と、新たにプロフィールに追加する内容を記入できるCreateProfile C...