LifeSports Application(ReactNative & Nest.js) - 10. auth-service(4)
16917 ワード
#1 user冗長モジュール
reactネイティブではlocalStorageはサポートされていないため、以下のパッケージをインストールします.
npm install @react-native-async-storage/async-storage
import { createAction, handleActions } from "redux-actions";
import AsyncStorage from '@react-native-async-storage/async-storage';
import { takeLatest } from "redux-saga/effects";
import * as authAPI from '../lib/api/auth';
import createRequestSaga, {
createRequestActionTypes,
} from "../lib/createRequestSaga";
const SAVE_USER = 'user/SAVE_USER';
const [
CHECK,
CHECK_SUCCESS,
CHECK_FAILURE
] = createRequestActionTypes('user/CHECK');
export const saveUser = createAction(SAVE_USER, user => user);
export const check = createAction(CHECK, userId => userId);
const checkSaga = createRequestSaga(CHECK, authAPI.check);
async function checkFailureSaga() {
try {
await AsyncStorage.removeItem('user');
} catch(e) {
console.log(e);
}
}
export function* userSaga() {
yield takeLatest(CHECK, checkSaga);
yield takeLatest(CHECK_FAILURE, checkFailureSaga);
}
const initialState = {
user: null,
checkError: null,
};
export default handleActions(
{
[SAVE_USER]: (state, { payload: user }) => ({
...state,
user,
}),
[CHECK_SUCCESS]: (state, { payload: user }) => ({
...state,
user,
checkError: null
}),
[CHECK_FAILURE]: (state, { payload: error }) => ({
...state,
user: null,
checkError: error,
}),
},
initialState,
);
userモジュールの機能を見てみましょう.saveUserの役割は,ログイン時にユーザの状態をreduce stateに保存することである.checkはuserIdを媒介とする方法であり,検査するユーザが適切なユーザであるか否かを確認するために用いられる.checkFailureSagaでは、ユーザーのステータスを削除する役割を果たします.次にauthモジュールで次のアクションを作成します.
...
const [
INFO,
INFO_SUCCESS,
INFO_FAILURE,
] = createRequestActionTypes('auth/INFO');
...
export const info = createAction(INFO, userId => userId);
...
const infoSaga = createRequestSaga(INFO, authAPI.getUser);
export function* authSaga() {
yield takeLatest(REGISTER, registerSaga);
yield takeLatest(LOGIN, loginSaga);
yield takeLatest(INFO, infoSaga);
}
...
const auth = handleActions(
{
...
[INFO_SUCCESS]: (state, { payload: auth }) => ({
...state,
authError: null,
auth,
}),
[INFO_FAILURE]: (state, { payload: error }) => ({
...state,
authError: error,
}),
},
initialState,
);
export default auth;
import { combineReducers } from "redux";
import { all } from "redux-saga/effects";
import auth, { authSaga } from './auth';
import loading from "./loading";
import user, { userSaga } from "./user";
const rootReducer = combineReducers({
auth,
loading,
user,
});
export function* rootSaga() {
yield all([
authSaga(),
userSaga()
]);
};
export default rootReducer;
import React from 'react';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
import createSagaMiddleware from 'redux-saga';
import rootReducer, { rootSaga } from './src/modules';
import { saveUser } from './src/modules/user';
import StackNavigatior from './src/navigator/MainNavigation';
const sagaMiddleware = createSagaMiddleware();
const store = createStore(
rootReducer,
composeWithDevTools(applyMiddleware(sagaMiddleware)),
);
async function loadUser() {
try {
const user = JSON.parse(await AsyncStorage.getItem('user'));
if(!user) {
return;
}
store.dispatch(saveUser(user));
const { userId } = user;
store.dispatch(check(userId));
} catch(e) {
console.log(e);
}
}
sagaMiddleware.run(rootSaga);
loadUser();
const App = () => {
return(
<Provider store={ store }>
<StackNavigatior />
</Provider>
);
};
export default App;
loadUserメソッドを作成して、アプリケーションの起動時にすぐにユーザーステータスをロードします....
export const getUser = async userId => client.get(`http://10.0.2.2:7000/auth-service/${userId}`, {
headers: {
'Authorization': 'Bearer ' + JSON.parse(await AsyncStorage.getItem('token'))
}
});
export const check = async userId => client.get(`http://10.0.2.2:7000/auth-service/${userId}/check`, {
headers: {
'Authorization': 'Bearer ' + JSON.parse(await AsyncStorage.getItem('token'))
}
});
checkメソッドはjwtタグをヘッダーに配置し、適切なユーザーであることを確認します.auth-serviceのコントローラにcheckに関連する方法を追加します.
...
@Controller('auth-service')
export class AppController {
...
@Get(':userId/check')
public async check(@Param('userId') userId: string): Promise<any> {
if(userId === null) {
return await Object.assign({
status: HttpStatus.UNAUTHORIZED,
payload: null,
message: "Not valid user"
});
}
const result: any = await this.userService.getUser(userId);
return await Object.assign({
status: HttpStatus.OK,
payload: Builder(ResponseUser).email(result.payload.email)
.nickname(result.payload.nickname)
.phoneNumber(result.payload.phoneNumber)
.userId(result.payload.userId)
.build(),
message: "Check user!"
});
}
}
ユーザーモジュールが完了しました.次に、ユーザーモジュールをログインフォームにロードします.import React, { useEffect, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { View, StyleSheet } from 'react-native';
import AsyncStorage from '@react-native-async-storage/async-storage';
import StyledBorderButton from '../../../styles/common/StyledBorderButton';
import StyledFullButton from '../../../styles/common/StyledFullButton';
import StyledTextInput from '../../../styles/common/StyledTextInput';
import palette from '../../../styles/palette';
import { changeField, info, initializeForm, login } from '../../../modules/auth';
import ErrorMessage from '../../../styles/common/ErrorMessage';
import { check } from '../../../modules/user';
const LoginForm = ({ navigation }) => {
...
useEffect(() => {
if(auth) {
const { userId } = auth;
dispatch(check(userId));
}
if(authError) {
setError("Not valid user, check again email and password");
return;
}
}, [auth, authError, dispatch]);
useEffect(async () => {
if(user) {
try {
setError(null);
dispatch(initializeForm('login'));
await AsyncStorage.setItem('user', JSON.stringify(user))
dispatch(initializeForm('auth'));
navigation.navigate('Tab');
} catch(e) {
console.log(e);
}
}
}, [user]);
useEffect(async () => {
if(auth) {
if(auth.token) {
const { token } = auth;
const { userId } = auth;
await AsyncStorage.setItem('token', JSON.stringify(token));
dispatch(info(userId));
}
}
}, [dispatch, auth]);
...
};
...
export default LoginForm;
では、これらのコードをもとに、ユーザーの状態がよく保存されているかどうかを確認します.ログインすると、ユーザーのステータスが表示されます.その後、私のページにユーザーのステータス値を表示し、ログアウトを実施しようとします.
import React from 'react';
import {
StyleSheet,
View,
Text
} from 'react-native';
import { useSelector } from 'react-redux';
import palette from '../../../styles/palette';
const MyPageHeader = () => {
const { user } = useSelector(({ user }) => ({ user: user.user }));
return(
<View style={ styles.container }>
<Text style={ styles.text }>
{ user ? user.nickname : null }
</Text>
<Text>
{ user ? user.email : null }
</Text>
</View>
);
};
const styles = StyleSheet.create({
container: {
width: 420,
height: 100,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: palette.gray[3],
},
text: {
fontWeight: 'bold',
fontSize: 17,
},
});
export default MyPageHeader;
MyPageHeaderに表示される簡単なユーザ情報を示し,ログアウトに関連するモジュールと方法を作成した.まず、authサービスコントローラで次の方法を定義します.
...
@Controller('auth-service')
export class AppController {
...
@Post('logout')
public async logout(): Promise<any> {
try {
return await Object.assign({
status: HttpStatus.NO_CONTENT,
payload: null,
message: "Success logout!"
});
} catch(err) {
return await Object.assign({
status: statusConstants.ERROR,
payload: null,
message: "Error message: " + err
});
}
}
}
次に、react nativeで関連付けられたAPIエンドポイントメソッドを定義します....
export const logout = () => client.post('http://10.0.2.2:7000/auth-service/logout');
モジュールをログアウトします.import { createAction, handleActions } from "redux-actions";
import AsyncStorage from '@react-native-async-storage/async-storage';
import { takeLatest, call } from "redux-saga/effects";
import * as authAPI from '../lib/api/auth';
import createRequestSaga, {
createRequestActionTypes,
} from "../lib/createRequestSaga";
...
const LOGOUT = 'user/LOGOUT';
...
export const logout = createAction(LOGOUT);
...
async function logoutSaga() {
try {
call(authAPI.logout);
await AsyncStorage.removeItem('user');
} catch(err) {
console.log(err);
}
}
export function* userSaga() {
yield takeLatest(CHECK, checkSaga);
yield takeLatest(CHECK_FAILURE, checkFailureSaga);
yield takeLatest(LOGOUT, logoutSaga);
}
...
export default handleActions(
{
...
[LOGOUT]: state => ({
...state,
user: null
}),
},
initialState,
);
このモジュールをLogoutButton構成部品にロードしてテストします.import React from 'react';
import {
StyleSheet,
Text,
TouchableOpacity
} from 'react-native';
import { useDispatch } from 'react-redux';
import { logout } from '../../../modules/user';
import palette from '../../../styles/palette';
const LogoutButton = () => {
const navigation = useNavigation();
const dispatch = useDispatch();
const onLogout = e => {
e.preventDefault();
dispatch(logout());
navigation.navigate('SignIn');
};
return(
<TouchableOpacity style={ styles.container }
onPress={ onLogout }
>
<Text style={ styles.text }>
Logout
</Text>
</TouchableOpacity>
);
};
const styles = StyleSheet.create({
container: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
width: 350,
height: 50,
borderRadius: 30,
borderColor: palette.blue[4],
borderWidth: 2,
backgroundColor: palette.white[0],
marginTop: 10,
},
text: {
fontWeight: 'bold',
fontSize: 15,
color: palette.blue[4],
},
});
export default LogoutButton;
テスト結果は,ユーザの状態が良好に削除されていることを示した.次の記事では、管理データに関連するローカルサーバを表示し、mapサービスからこのデータを取得しようとします.
Reference
この問題について(LifeSports Application(ReactNative & Nest.js) - 10. auth-service(4)), 我々は、より多くの情報をここで見つけました https://velog.io/@biuea/LifeSports-ApplicationReactNative-Nest.js-10.-auth-service4テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol