質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Redux

Reduxは、JavaScriptアプリケーションの状態を管理するためのオープンソースライブラリです。ReactやAngularで一般的にユーザーインターフェイスの構築に利用されます。

非同期処理

非同期処理とは一部のコードを別々のスレッドで実行させる手法です。アプリケーションのパフォーマンスを向上させる目的でこの手法を用います。

React.js

Reactは、アプリケーションのインターフェースを構築するためのオープンソースJavaScriptライブラリです。

Q&A

解決済

2回答

4112閲覧

react-reduxにて、storeの値を取得してからactionをdispatchする方法

Toshiyuki023

総合スコア3

Redux

Reduxは、JavaScriptアプリケーションの状態を管理するためのオープンソースライブラリです。ReactやAngularで一般的にユーザーインターフェイスの構築に利用されます。

非同期処理

非同期処理とは一部のコードを別々のスレッドで実行させる手法です。アプリケーションのパフォーマンスを向上させる目的でこの手法を用います。

React.js

Reactは、アプリケーションのインターフェースを構築するためのオープンソースJavaScriptライブラリです。

0グッド

0クリップ

投稿2020/09/11 00:12

編集2020/09/11 00:18

環境

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が取得されないまま処理が進んでしまうことで、uidnullになり、結果、actionauthSuccessによってstateuid : nullに書き換えられてしまうことです。

最終的に私が行いたいのは、uidをログインしているユーザーのものにすることです。
ログイン時、または、新規登録時にuidを更新することには成功しているのですが(actionauthSignup, authLogin参照)、画面遷移を行うとuidnullに上書きされてしまいます。
原因は、App.jsx内でauthCheckStateがdispatchされた時に、authSuccessの引数の一つであるuidが、ログイン済みユーザーのIDを持たないままdispatchされたことにより、stateuidnullへ変わってしまうために起きていると私は考察しています。

解決法として、authCheckState内でstoreからuidを取得してきて、それをauthSuccessの引数に入れるということを思いつき、async/awaitで実装をしてみました。しかし、依然としてuidnullのままだったため、store.getState()の処理の完了が待たれずにauthSuccessが実行されている様に思われます。

どの様にすれば、uidauthSuccess間の非同期を制御し、uidauthSuccessに入れることができるかを教えていただきたいです。

分かりづらい説明となってしまい、大変申し訳ありません。
ご回答いただければ幸いです。
よろしくお願いいたします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

修正の一案として、以下のようにしてみるのはいかがでしょうか?

  • getUserId の成功時に user id も localStorage に保存しておく。
  • authCheckState では localStorage に user id が入っているかを確認し、
  • 入っていれば、 authSuccess をdispatch する。

コードではこんな感じの修正になります。

diff

1 export const getUserId = (username) => { 2 return (dispatch) => { 3 axios.get('http://localhost:8000/api/user').then((res) => { 4 const users = res.data; 5 const currentUser = users.filter((user) => user.username === username)[0]; 6 const uid = currentUser.id; 7+ localStorage.setItem('userId', uid); 8 dispatch(authSuccess(localStorage.getItem('token'), uid)); 9- }); 10+ }) 11+ .catch((err) => { 12+ localStorage.removeItem('userId'); 13+ }); 14 }; 15 };

diff

1 // uidを取得してからauthSuccessを実行させたいです。 2- const uid = await store.getState().uid; 3- dispatch(authSuccess(token, uid)); 4+ const uid = localStorage.getItem('userId'); 5+ if (uid) { 6+ dispatch(authSuccess(token, uid)); 7+ }

補足として、このご質問のような状況のときに、役に立ちそうなモジュールを紹介します。

ログインAPIから返されたトークンなどのログイン情報を、localStorageに入れて永続化させるというのはよくあるケースです。その際に、redux state に入っているログイン情報とlocalStorageに保存しているログイン情報との整合性を保つコードを自分で書くのは、様々なログイン関連のユースケースに対応していくうちにコードが煩雑になってきがちです。上記に挙げたコード修正案も、そのような煩雑さが今後も増大していくことを予感させるものになっている、とも言えます。この問題を解消するために、localStorage への永続化を任せられる redux-persist を使うとよいかもしれません。Qiita にも、「使ってみました」という趣旨の記事がいくつか 投稿されています。

以上、参考になれば幸いです。

投稿2020/09/11 02:43

編集2020/09/11 04:08
jun68ykt

総合スコア9058

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Toshiyuki023

2020/09/13 00:27

ご回答いただき誠にありがとうございます。 ご助言の甲斐あり、実装に成功することができました。また、有益なパッケージも紹介していただき、本当にありがとうございます。 まだまだ初心者ですので、質問することが多々あると思いますが、また機会があればご回答いただければ幸いです。 ありがとうございました。
jun68ykt

2020/09/13 00:45

どういたしまして > 実装に成功することができました。 とのことで、よかったです????
guest

0

reduxの処理内で非同期を扱う場合redux-sagaの導入がおすすめです。

Aアクションのdispatchを待ってからBアクションをdispatchや、処理中でstoreから特定の値を取得などを管理できます。

下記記事が参考になるかと思います。

投稿2020/09/11 00:17

nekoniki

総合スコア2409

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問