99日目:ユーザーのログイン&ログアウト、ナビゲーションバーのリンクを完成させる(Section 8 React User Authentication完了)

この日は6つのファイルにコードを書き足しながら、ユーザーのログイン・ログアウト、そしてナビゲーションバーのリンクを完成させました。

①src/actions/auth.jsに./typesからのインポートとLogin user & Logoutのコードの書き足しを行う

 

import axios from "axios";
import { setAlert } from "./alert";
import {
  REGISTER_SUCCESS,
  REGISTER_FAIL,
  USER_LOADED,
  AUTH_ERROR,
  LOGIN_SUCCESS,
  LOGIN_FAIL,
  LOGOUT
} from "./types";
import setAuthToken from "../utils/setAuthToken";

// Load User
export const loadUser = () => async dispatch => {
  if (localStorage.token) {
    setAuthToken(localStorage.token);
  }

  try {
    const res = await axios.get("./api/auth");

    dispatch({
      type: USER_LOADED,
      payload: res.data
    });
  } catch (err) {
    dispatch({
      type: AUTH_ERROR
    });
  }
};

//Register User
export const register = ({ name, email, password }) => async dispatch => {
  const config = {
    headers: {
      "Content-Type": "application/json"
    }
  };

  const body = JSON.stringify({ name, email, password });

  try {
    const res = await axios.post("/api/users", body, config);

    dispatch({
      type: REGISTER_SUCCESS,
      payload: res.data
    });

    dispatch(loadUser());
  } catch (err) {
    const errors = err.response.data.errors;

    if (errors) {
      errors.forEach(error => dispatch(setAlert(error.msg, "danger")));
    }

    dispatch({
      type: REGISTER_FAIL
    });
  }
};

//Login User
export const login = (email, password) => async dispatch => {
  const config = {
    headers: {
      "Content-Type": "application/json"
    }
  };

  const body = JSON.stringify({ email, password });

  try {
    const res = await axios.post("/api/auth", body, config);

    dispatch({
      type: LOGIN_SUCCESS,
      payload: res.data
    });

    dispatch(loadUser());
  } catch (err) {
    const errors = err.response.data.errors;

    if (errors) {
      errors.forEach(error => dispatch(setAlert(error.msg, "danger")));
    }

    dispatch({
      type: LOGIN_FAIL
    });
  }
};

// Logout / Clear Profile
export const logout = () => dispatch => {
  dispatch({ type: LOGOUT });
};

今回書き足しをしたのは、最初のimport {} from "./types"; 部分のLOGIN_SUCCESS, LOGIN_FAIL, LOGOUT 部分です。

そして、コメントアウトしているLogin User & Logoutの部分です。Login Userも部分については、まずはRegister Userからコードを持ってきて、必要ないものを消去したり少し書き換えるだけ、という方法をとりました。

②src/actions/types.js のコードを書き足す。

先程の、src/actions/auth.jsの最初の部分でimport {} from "./types"; とコードを書いた、ということはtypes.jsも書き足しが必要ですよね。というわけで、こちらにもログイン・ログアウト情報などを書き加えました。

export const SET_ALERT = "SET_ALERT";
export const REMOVE_ALERT = "REMOVE_ALERT";
export const REGISTER_SUCCESS = "REGISTER_SUCCESS";
export const REGISTER_FAIL = "REGISTER_FAIL";
export const USER_LOADED = "USER_LOADED";
export const AUTH_ERROR = "AUTH_ERROR";
export const LOGIN_SUCCESS = "LOGIN_SUCCESS";
export const LOGIN_FAIL = "LOGIN_FAIL";
export const LOGOUT = "LOGOUT";

③src/reducers/auth.jsにも、LOGIN_SUCCESS, LOGIN_FAIL, LOGOUTのコードを書き足す

こちらはシンプルで、import {} from "..actions/types"; の部分へのコード書き足しと、switchのcaseの部分への書き足しのみとなりました。

import {
  REGISTER_SUCCESS,
  REGISTER_FAIL,
  USER_LOADED,
  AUTH_ERROR,
  LOGIN_SUCCESS,
  LOGIN_FAIL,
  LOGOUT
} from "../actions/types";

const initialState = {
  token: localStorage.getItem("token"),
  isAuthenticated: null,
  loading: true,
  user: null
};

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:
      localStorage.removeItem("token");
      return {
        ...state,
        token: null,
        isAuthenticated: false,
        loading: false
      };
    default:
      return state;
  }
}

④ component/auth/Login.jsへのコード書き加え(Login.jsの完成)

Login.jsへは沢山コードを書き加えました。最初のimport部分には、Redirect, connect, PropTypes, loginを書き加え、それに合わせて、最後11行、Login.Proptypesから始まるコードを書き加えています。点と点だったものがやっと繋ぎ合わさり、Login.jsファイルは完成となりました!

import React, { Fragment, useState } from "react";
import { Link, Redirect } from "react-router-dom";
import { connect } from "react-redux";
import PropTypes from "prop-types";
import { login } from "../../actions/auth";

const Login = ({ login, isAuthenticated }) => {
  const [formData, setFormData] = useState({
    email: "",
    password: ""
  });

  const { email, password } = formData;

  const onChange = e =>
    setFormData({ ...formData, [e.target.name]: e.target.value });

  const onSubmit = async e => {
    e.preventDefault();
    login(email, password);
  };

  if (isAuthenticated) {
    return ;
  }

  return (
//fragment内のコードがブログ上では上手く表示されないため割愛。(Sign in, Login, Sign up部分の表示をするためのコード) 
  );
};
Login.propTypes = {
  login: PropTypes.func.isRequired,
  isAuthenticated: PropTypes.bool
};
const mapStateToProps = state => ({
  isAuthenticated: state.auth.isAuthenticated
});
export default connect(
  mapStateToProps,
  { login }
)(Login);

最初は/dashboardへのリダイレクトが上手くいかず少し焦ったのですが、もう一度書き直しをすることで上手くいきました。

⑤components/auth/Register.jsを完成させる

Register.jsもLogin.jsと同様、import部分に Redirectを書き加えています。

また、const RegisterにはisAuthenticatedを書き加え、'/dashboard'にリダイレクトするようにしました。

最後の方にはconst mapStateToPropsを作成しています。

import React, { Fragment, useState } from "react";
import { connect } from "react-redux";
import { Link, Redirect } from "react-router-dom";
import { setAlert } from "../../actions/alert";
import { register } from "../../actions/auth";
import PropTypes from "prop-types";

const Register = ({ setAlert, register, isAuthenticated }) => {
  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 = async e => {
    e.preventDefault();
    if (password !== password2) {
      setAlert("Passwords do not match", "danger");
    } else {
      register({ name, email, password });
    }
  };

  if (isAuthenticated) {
    return ;//Redirectで/dashboardへ。
  }

  return (
   //Fragment内のコードがブログ上で上手く表示されないため割愛。Sign up, Create Your Account, フォームのonSubmit等のコード。 
  );
};

Register.propTypes = {
  setAlert: PropTypes.func.isRequired,
  register: PropTypes.func.isRequired,
  isAuthenticated: PropTypes.bool
};

const mapStateToProps = state => ({
  isAuthenticated: state.auth.isAuthenticated
});

export default connect(
  mapStateToProps,
  { setAlert, register }
)(Register);

⑥src/components/layout/Navbar.js ナビゲーションバーにimportとログアウトコード書き加え

Navbar.jsは元々ファイルを作成した時にはシンプルにimport Reactと{Link}周りのコードをを書いていたのみだったのですが、{connect} , PropTypes, {logout}を書き加えました。

また、const Navbarなどでログアウトもできるようにコードを書き加えました。最終的にこのファイルでもpropTYpes mapStateToProps等を利用するようにしています。

 

import React, { Fragment } from "react";
import { Link } from "react-router-dom";
import { connect } from "react-redux";
import PropTypes from "prop-types";
import { logout } from "../../actions/auth";

const Navbar = ({ auth: { isAuthenticated, loading }, logout }) => {
  const authLinks = (
  //onClickでログアウト(割愛)

); const guestLinks = (
//Developers, Register, Loginへのリンク(割愛)
); return (

); }; Navbar.propTypes = { logout: PropTypes.func.isRequired, auth: PropTypes.object.isRequired }; const mapStateToProps = state => ({ auth: state.auth }); export default connect( mapStateToProps, { logout } )(Navbar);

本日の学習の感想(99日目)

ファイルの数が増えてきたのでtreeを書いて全体像を把握しました。割とシンプルなアプリケーションになる予定なのですが、それでもコードは沢山書かなければならないのだなぁという印象です。繰り返しに近いことも多いので慣れてきた部分と、まだはっきりわかっていない部分があるので、復習しながら学習を進めたいです。

次回で、まずは目標としていた100日目に到達します!できなかったことより、できたことを数えていくのが(プログラミングに限らずですが)学習を継続していくコツだと思いました。

現在使用している教材と学習時間:本日の学習時間2.5時間

Udemy:MERN Stack Front To Back: Full Stack React, Redux & Node.js by Brad Traversy

★Section8:React User Authentication

-Lec 42 User Login

-Lec 43 Logout & Navbar Links (ここまで完了。Section8終了)

進捗状況:59%

学習時間2.5時間

~本日は休みにしている教材~

Udemy:The Complete Web Developer: Zero to Mastery by Andrei Neagoie

進捗状況: 92%

★参考にした本★

「React.js & Next.js超入門」掌田津耶乃 (秀和システム)

Section4-1: Reduxを使ってみよう

★参照記事★

Quiita参照記事⇒[axios] axios の導入と簡単な使い方

関連キーワード
  • 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コマンドではまったところと解決方法
おすすめの記事