環境
axios
: "0.20.0",
react
: "16.13.1",
react-dom
: "16.13.1",
react-redux
: "7.2.1",
redux-thnx
: "2.3.0"
コード
actions
1import * as actionTypes from './actionType'; 2import axios from 'axios'; 3import { useSelector } from 'react-redux'; 4import { store } from '../../index'; 5 6export const authStart = () => { 7 return { 8 type: actionTypes.AUTH_START, 9 }; 10}; 11 12export const authSuccess = (token,uid) => { 13 return { 14 type: actionTypes.AUTH_SUCCESS, 15 token: token, 16 uid:uid 17 }; 18}; 19 20export const setUid = (uid) => { 21 return { 22 type: actionTypes.SET_UID, 23 uid: uid, 24 }; 25}; 26 27export const authFail = (error) => { 28 return { 29 type: actionTypes.AUTH_FAIL, 30 error: error, 31 }; 32}; 33 34export const logout = () => { 35 localStorage.removeItem('token'); 36 localStorage.removeItem('expirationDate'); 37 axios 38 .post('http://localhost:8000/rest-auth/logout/') 39 .then((res) => { 40 console.log(res.json()); 41 }) 42 .catch((err) => { 43 console.log(err); 44 }); 45 return { 46 type: actionTypes.AUTH_LOGOUT, 47 }; 48}; 49 50// ======================== ======================== ======================== ======================== 51 52// 以下redux-thunx用のasync action creator ========= ============ =============- =========== 53 54export const checkAuthTimeout = (expirationTime) => { 55 return (dispatch) => { 56 setTimeout(() => { 57 dispatch(logout()); 58 }, expirationTime * 1000); 59 }; 60}; 61 62export const getUserId = (username) => { 63 return (dispatch) => { 64 axios.get('http://localhost:8000/api/user').then((res) => { 65 const users = res.data; 66 const currentUser = users.filter((user) => user.username === username)[0]; 67 const uid = currentUser.id; 68 dispatch(authSuccess(localStorage.getItem('token'), uid)); 69 }); 70 }; 71}; 72 73export const authLogin = (username, password) => { 74 return (dispatch) => { 75 dispatch(authStart()); 76 axios 77 .post('http://localhost:8000/rest-auth/login/', { 78 username: username, 79 password: password, 80 }) 81 .then((res) => { 82 const token = res.data.key; 83 const expirationDate = new Date(new Date().getTime() + 3600 * 1000); 84 localStorage.setItem('token', token); 85 localStorage.setItem('expirationDate', expirationDate); 86 dispatch(getUserId(username)); 87 dispatch(checkAuthTimeout(3600)); 88 }) 89 .catch((err) => { 90 dispatch(authFail(err)); 91 }); 92 }; 93}; 94 95export const authSignup = (username, email, password) => { 96 return (dispatch) => { 97 dispatch(authStart()); 98 axios 99 .post('http://localhost:8000/rest-auth/registration/', { 100 username: username, 101 email: email, 102 password: password, 103 }) 104 .then((res) => { 105 const token = res.data.key; 106 const expirationDate = new Date(new Date().getTime() + 3600 * 1000); 107 localStorage.setItem('token', token); 108 localStorage.setItem('expirationDate', expirationDate); 109 // getUserId内でauthSuccessが実行され、auth_SUCCESSへuid, tokenがセット 110 dispatch(getUserId(username)); 111 dispatch(checkAuthTimeout(3600)); 112 }) 113 .catch((err) => { 114 dispatch(authFail(err)); 115 }); 116 }; 117}; 118 119export const authCheckState = () => { 120 return async(dispatch) => { 121 const token = localStorage.getItem('token'); 122 if (token === undefined) { 123 dispatch(logout()); 124 } else { 125 const expirationDate = new Date(localStorage.getItem('expirationDate')); 126 if (expirationDate <= new Date()) { 127 dispatch(logout()); 128 } else { 129 // 130 //uidを取得してからauthSuccessを実行させたいです。 131 const uid = await store.getState().uid; 132 dispatch(authSuccess(token, uid)); 133 console.log("uid is " + uid + " and token is " + token) 134 dispatch(checkAuthTimeout((expirationDate.getTime() - new Date().getTime()) / 1000)); 135 } 136 } 137 }; 138}; 139
reducer
1import * as actionTypes from './actionType'; 2import { updateObject } from '../utility'; 3 4const initialState = { 5 token: null, 6 error: null, 7 loading: false, 8 uid: null, 9}; 10 11const authStart = (state, action) => { 12 return updateObject(state, { 13 error: null, 14 loading: true, 15 }); 16}; 17 18const authSuccess = (state, action) => { 19 // updateObjectによりstateの更新をfunction化 20 return updateObject(state, { 21 token: action.token, 22 error: null, 23 loading: false, 24 uid : action.uid 25 }); 26}; 27 28const authFail = (state, action) => { 29 return updateObject(state, { 30 error: action.error, 31 loading: false, 32 }); 33}; 34 35const authLogout = (state, action) => { 36 return updateObject(state, { 37 token: null, 38 uid: null, 39 }); 40}; 41 42const setUid = (state, action) => { 43 return updateObject(state, { 44 uid: action.uid, 45 }); 46}; 47 48export const reducer = (state = initialState, action) => { 49 switch (action.type) { 50 case actionTypes.AUTH_START: 51 return authStart(state, action); 52 case actionTypes.AUTH_SUCCESS: 53 return authSuccess(state, action); 54 case actionTypes.AUTH_FAIL: 55 return authFail(state, action); 56 case actionTypes.AUTH_LOGOUT: 57 return authLogout(state, action); 58 default: 59 return state; 60 } 61}; 62 63export default reducer; 64
App
1import React, { Component } from 'react'; 2import { BrowserRouter as Router, Route } from 'react-router-dom'; 3import { connect } from 'react-redux'; 4import * as actions from './reducks/auth/actions'; 5import { Link } from 'react-router-dom'; 6 7import Register from './containers/Pages/Register'; 8import Login from './containers/Pages/Login'; 9import Add_Want_Item from './containers/Pages/Add_Want_Item'; 10 11class App extends Component { 12 componentDidMount() { 13 // tokenがローカルに存在してるかの確認、expirationdateの期限確認 14 this.props.onTryAutoSignup(); 15 } 16 17 render() { 18 return ( 19 <Router> 20 {/* Route内のRoutePassはpropsを渡す役割を果たす */} 21 <Route 22 exact 23 path="/registration" 24 render={(routeProps) => <Register {...routeProps} {...this.props} />} 25 /> 26 <Route 27 exact 28 path="/login" 29 render={(routeProps) => <Login {...routeProps} {...this.props} />} 30 /> 31 <Route exact path="/user/want/add" component={Add_Want_Item} /> 32 </Router> 33 ); 34 } 35} 36 37const mapStateToProps = (state) => { 38 return { 39 isAuthenticated: state.token !== null, 40 }; 41}; 42 43const mapDispatchToProps = (dispatch) => { 44 return { 45 onTryAutoSignup: () => dispatch(actions.authCheckState()), 46 }; 47}; 48 49export default connect(mapStateToProps, mapDispatchToProps)(App);
やりたいこと
actions
内で、ストアからのuid
取得を待ち、uid
を取得した後にuid
を引数とするauthSuccess
をdispatchしたいです。
問題点
actionの一つauthCheckState
内におけるdispatch(authSuccess(token, uid))
でuid
が取得されないまま処理が進んでしまうことで、uid
がnull
になり、結果、actionauthSuccess
によってstate
がuid : null
に書き換えられてしまうことです。
最終的に私が行いたいのは、uid
をログインしているユーザーのものにすることです。
ログイン時、または、新規登録時にuid
を更新することには成功しているのですが(actionauthSignup
, authLogin
参照)、画面遷移を行うとuid
がnull
に上書きされてしまいます。
原因は、App.jsx
内でauthCheckState
がdispatchされた時に、authSuccess
の引数の一つであるuid
が、ログイン済みユーザーのIDを持たないままdispatchされたことにより、state
のuid
がnull
へ変わってしまうために起きていると私は考察しています。
解決法として、authCheckState
内でstore
からuid
を取得してきて、それをauthSuccess
の引数に入れるということを思いつき、async/await
で実装をしてみました。しかし、依然としてuid
はnull
のままだったため、store.getState()
の処理の完了が待たれずにauthSuccess
が実行されている様に思われます。
どの様にすれば、uid
とauthSuccess
間の非同期を制御し、uid
をauthSuccess
に入れることができるかを教えていただきたいです。
分かりづらい説明となってしまい、大変申し訳ありません。
ご回答いただければ幸いです。
よろしくお願いいたします。
回答2件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2020/09/13 00:27
2020/09/13 00:45