افزودن احراز هویت به اپلیکیشن ری اکت نیتیو — از صفر تا صد
با استفاده از قلابهای ریاکت و React Context API به همراه Expo یا React Native CLI میتوان هویت کاربران اپلیکیشنهای ریاکت نیتیو را به روشی مدرن احراز کرد. به این ترتیب در این راهنما با روش افزودن احراز هویت به اپلیکیشن ری اکت نیتیو آشنا خواهیم شد به طوری که کاربران بتوانند کارهای زیر را انجام دهند:
- ثبت نام (یک ایمیل تأیید هویت پس از ثبت نام ارسال میشود).
- لاگین (کاربران میتوانند در صورت تأیید کردن ایمیل خود وارد حسابشان شوند)
- بهروزرسانی پروفایل
- بازیابی رمز عبور
برای پیگیری این راهنما به Authentication API نیاز دارید که میتوانید با توجه به راهنماییهای این مطلب (+) ایجاد کرده و یا از دموی ارائه شده در این صفحه (+) استفاده کنید.
همچنین باید نرمافزارهای زیر را روی سیستم خود داشته باشید:
بخش اول: راهاندازی پروژه، وابستگی و ساختار فایل
در این بخش به توضیح مراحل تنظیمات اولیه پروژه، نصب وابستگیها و سازماندهی فایلها و پوشهها میپردازیم.
گام 1: ایجاد پروژه
برای ایجاد پروژه باید دستور زیر را اجرا کنید. یک قالب خالی را انتخاب کنید و نام اپلیکیشن خود را تعیین نمایید:
Expo
$ expo init react-native-authentication $ cd react-native-authentication
React Native CLI
$ npx react-native init ReactNativeAuthentication $ cd ReactNativeAuthentication
گام 2: نصب وابستگیها
ما از پکیجهای زیر استفاده خواهیم کرد.
- Axios (+) – یک کلاینت HTTP مبتنی بر Promise برای ایجاد فراخوانیها به API است.
- فرم ابتدایی ریاکت نیتیو (+).
$ npm i --save axios react-native-basic-form react-native-elements
وابستگیهای ناوبری نیز به شرح زیر هستند:
- React Navigation (+) – برای ناوبری درون اپلیکیشن استفاده میشود.
- React Navigation Stack (+)- ناوبری استک است که روی سیستمهای عامل اندروید و iOS استفاده میشود.
$ npm i --save react-navigation react-navigation-stack
- Expo
$ expo install react-native-gesture-handler react-native-reanimated react-native-screens react-native-safe-area-context @react-native-community/masked-view
- React Native CLI
$ npm install react-native-gesture-handler react-native-reanimated react-native-screens react-native-safe-area-context @react-native-community/masked-view --save
- لینک کردن کتابخانهها
$ cd ios; pod install; cd..
- آیکونهای بُرداری
npm i --save react-native-vector-icons react-native link react-native-vector-icons
گام 3: ایجاد ساختار پوشه
در ریشه پروژه، پوشهای به نام app ایجاد کنید. درون این پوشه، پوشههای زیر را ایجاد کنید:
- Components – شامل همه کامپوننتها خواهد بود.
- Scenes – شامل همه صحنهها خواهد بود.
- Routes – مسیرهای اپلیکیشن را شامل میشود.
- Services – شامل کارکردهایی برای دسترسی به API بکاند خواهد بود.
مطمئن شوید که در ریشه پروژه هستید و دستورهای زیر را اجرا کنید:
$ mkdir app && cd app $ mkdir providers services components scenes routes
بخش دوم
در این بخش به بررسی ثابتها، ارائهدهنده، ردیوسر و سرویسها میپردازیم.
گام 4: پیکربندی
در پوشه app دو فایل به نامهای constants.js و theme.js ایجاد میکنیم. فایل constants.js شامل url API، نقاط انتهایی آن و دیگر متغیرهای پیکربندی اعلان شده خواهد بود:
- فایل constants.js
1import React from 'react';
2
3//API URL
4export const API_URL = 'https://mesannodejsapiwithverification.herokuapp.com/api';
5
6//API End Points
7export const REGISTER = `${API_URL}/auth/register`;
8export const LOGIN = `${API_URL}/auth/login`;
9export const UPDATE_PROFILE = `${API_URL}/user`;
10export const UPLOAD_IMAGE = `${API_URL}/user/upload`;
11export const FORGOT_PASSWORD = `${API_URL}/auth/recover`;
- فایل theme.js
1import {Platform} from "react-native";
2
3export let font = Platform.OS === 'ios' ? 'HelveticaNeue' : 'Roboto';
4export let titleColor = '#363434';
5
6//Nav Shared Styles
7export let headerStyle = {backgroundColor: '#fff', borderBottomWidth:0, shadowColor: 'transparent'};
8export let headerTitleStyle = {fontWeight: 'bold', fontSize: 17, fontFamily: font, color: titleColor};
9
10export const imageOptions = {allowsEditing: true, aspect: [4, 3]};
گام 5: ارائهدهنده
در پوشه providers یک فایل به نام auth.js و با محتوای زیر ایجاد کنید:
- فایل auth.js
1import React, {useMemo, useReducer, useContext} from 'react';
2import {AsyncStorage} from "react-native";
3import axios from "axios";
4
5//IMPORT REDUCER, INITIAL STATE AND ACTION TYPES
6import reducer, {initialState, LOGGED_IN, LOGGED_OUT} from "./reducer";
7
8// CONFIG KEYS [Storage Keys]===================================
9export const TOKEN_KEY = 'token';
10export const USER_KEY = 'user';
11export const keys = [TOKEN_KEY, USER_KEY];
12
13// CONTEXT ===================================
14const AuthContext = React.createContext();
15
16function AuthProvider(props) {
17 const [state, dispatch] = useReducer(reducer, initialState || {});
18
19 // Get Auth state
20 const getAuthState = async () => {
21 try {
22 //GET TOKEN && USER
23 let token = await AsyncStorage.getItem(TOKEN_KEY);
24 let user = await AsyncStorage.getItem(USER_KEY);
25 user = JSON.parse(user);
26
27 if (token !== null && user!== null) await handleLogin({token, user});
28 else await handleLogout();
29
30 return {token, user};
31 } catch (error) {
32 throw new Error(error)
33 }
34 };
35
36 // Handle Login
37 const handleLogin = async (data) => {
38 try{
39 //STORE DATA
40 let {token, user} = data;
41 let data_ = [[USER_KEY, JSON.stringify(user)], [TOKEN_KEY, token]];
42 await AsyncStorage.multiSet(data_);
43
44 //AXIOS AUTHORIZATION HEADER
45 axios.defaults.headers.common["Authorization"] = `Bearer ${data.token}`;
46
47 //DISPATCH TO REDUCER
48 dispatch({type: LOGGED_IN, user:data.user});
49 }catch (error) {
50 throw new Error(error);
51 }
52 };
53
54 // Handle Logout
55 const handleLogout = async () => {
56 try{
57
58 //REMOVE DATA
59 await AsyncStorage.multiRemove(keys);
60
61 //AXIOS AUTHORIZATION HEADER
62 delete axios.defaults.headers.common["Authorization"];
63
64 //DISPATCH TO REDUCER
65 dispatch({type: LOGGED_OUT});
66 }catch (error) {
67 throw new Error(error);
68 }
69 };
70
71 //UPDATE USER LOCAL STORAGE DATA AND DISPATCH TO REDUCER
72 const updateUser = async (user) => {
73 try {
74 await AsyncStorage.setItem(USER_KEY, JSON.stringify(user));
75 dispatch({type: LOGGED_IN, user}); //DISPATCH TO REDUCER
76 } catch (error) {
77 throw new Error(error);
78 }
79 };
80
81 const value = useMemo(() => {
82 return {state, getAuthState, handleLogin, handleLogout, updateUser};
83 }, [state]);
84
85 return (
86 <AuthContext.Provider value={value}>
87 {props.children}
88 </AuthContext.Provider>
89 );
90}
91
92const useAuth = () => useContext(AuthContext);
93export { AuthContext, useAuth }
94export default AuthProvider;
در ابتدای فایل، context ایجاد شده است. reducer و initial state از فایل ردیوسری که در ادامه ایجاد خواهد شد ایمپورت میشوند و به قلابی به نام useReducer ارسال میشوند که حالت جاری را به همراه متد دیسپچ مشابه ریداکس بازگشت میدهد.
در ادامه تابعهای مختلفی اعلان یافته و به صورت مقدار به provider ارسال شدهاند. تابع getAuthState دادههای کاربر را در صورت لاگین کردن بازگشت میدهد. تابع handleLogin توکن و دادههای کاربران را ذخیره کرده، هدر Axios Authorization را تنظیم نموده و دادههای کاربر را به ردیوسر هدایت میکند تا ذخیره شوند. تابع handleLogout دقیقاً کاری مخالف تابع قبلی انجام میدهد. تابع updateUser دادههای کاربران را در فضای لوکال ذخیره کرده و دادههای جدید را به ردیوسر ارسال میکند تا ذخیره شوند.
در انتهای فایل فوق یک شیء جدید useAuth با استفاده از قلابی به نام useContext که قبلاً در AuthContext اعلان کردیم، ایجاد شده است. این شیء مقدار context کنونی را برای context ما بیان میکند و همراه با AuthContext و Provider اکسپورت میشود.
گام 6: ردیوسر
در پوشه app فایلی به نام reducer.js ایجاد کنید. این ردیوسر مسئول بهروزرسانی حالت اپلیکیشن بر مبنای اکشن ارسالی است.
1//Action Types
2export const LOGGED_IN = `auth/LOGGED_IN`;
3export const LOGGED_OUT = `auth/LOGGED_OUT`;
4
5export const initialState = {
6 isLoggedIn: false,
7 user: null
8};
9
10//REDUCER
11const authReducer = (state = initialState, action) => {
12 switch (action.type) {
13 case LOGGED_IN:{
14 let { user } = action;
15
16 return {...state, isLoggedIn: true, user};
17 }
18
19 case LOGGED_OUT:{
20 return {...state, ...initialState};
21 }
22
23 default:
24 return state;
25 }
26};
27
28export default authReducer;
گام 7: سرویسها
در پوشه services فایلی جدید به نام auth.js ایجاد کنید. این فایل شامل همه تابعهایی خواهد بود که برای دسترسی به نقاط انتهایی Authentication API مورد نیاز هستند:
- فایل auth.js
1import axios from 'axios';
2
3import * as c from '../constants';
4
5export async function register(data){
6 try{
7 let res = await axios.post(c.REGISTER, data);
8
9 return res.data;
10 }catch (e) {
11 throw handler(e)
12 }
13}
14
15export async function login(data){
16 try{
17 let res = await axios.post(c.LOGIN, data);
18
19 return res.data;
20 }catch (e) {
21 throw handler(e);
22 }
23}
24
25export async function forgotPassword(data) {
26 try {
27 let res = await axios.post(c.FORGOT_PASSWORD, data);
28
29 return res.data;
30 } catch (e) {
31 throw handler(e);
32 }
33}
34
35export async function updateProfile(userId, data){
36 try{
37 const options = {
38 headers: {
39 Accept: "application/json",
40 "Content-Type": "multipart/form-data"
41 }
42 };
43
44 const form_data = new FormData();
45 for ( let key in data )
46 form_data.append(key, data[key]);
47
48 let res = await axios.put(`${c.UPDATE_PROFILE}/${userId}`, form_data, options);
49 return res.data;
50 }catch (e) {
51 throw handler(e);
52 }
53}
54
55export function handler(err) {
56 let error = err;
57
58 if (err.response && err.response.data.hasOwnProperty("message"))
59 error = err.response.data;
60 else if (!err.hasOwnProperty("message")) error = err.toJSON();
61
62 return new Error(error.message);
63}
در کد فوق تابع register درخواست API برای ثبت نام کاربر را ارائه میکند و پارامتر آن دادههای کاربر است. تابع login درخواست ورود کاربر را عرضه میکند و پارامتر آن دادههای کاربران است. تابع forgotPassword یک درخواست API برای ورود کاربر ایجاد میکند و پارامتر آن email در یک شیء است. تابع updateProfile یک درخواست API برای بهروزرسانی دادهای کاربر ایجاد میکند و پارامترهای آن userId و data است. این تابع روی همه دادههای ارسالی چرخش کرده و یک شیء به نام FormData میسازد. یک شیء axios options با نوع محتوا به صورت multipart/form-data نیز ایجاد میشود تا امکان آپلود تصویر وجود داشته باشد.
بخش سوم
در این بخش کامپوننتها، صحنهها و مسیرها را تنظیم خواهیم کرد.
گام 8: کامپوننتها
در پوشه components فایلی به نام CTA.js با محتوای زیر ایجاد کنید:
1import React from 'react';
2import {View, Text, TouchableOpacity, StyleSheet} from 'react-native';
3
4export default function CTA({title, ctaText, onPress, style, titleStyle, ctaStyle}){
5 return (
6 <View style={[styles.footer, style]}>
7 {
8 title &&
9 <Text style={[styles.footerText, titleStyle, ctaText && {marginRight: 5}]}>
10 {title}
11 </Text>
12 }
13
14 {
15 ctaText &&
16 <TouchableOpacity onPress={onPress}>
17 <Text style={[styles.footerCTA, ctaStyle]}>
18 {ctaText}
19 </Text>
20 </TouchableOpacity>
21 }
22 </View>
23 )
24};
25
26CTA.defaultProps = {
27 title: null,
28 ctaText: null,
29 onPress:{},
30 style: {},
31 titleStyle: {},
32 ctaStyle: {},
33};
34
35const styles = StyleSheet.create({
36 footer: {
37 flexDirection: "row",
38 justifyContent: "center",
39 alignItems: "center",
40 },
41
42 footerText: {
43 fontSize: 16,
44 fontFamily: "Helvetica Neue",
45 color: "#636466"
46 },
47
48 footerCTA: {
49 fontSize: 16,
50 color: "#733AC2",
51 fontWeight: "500",
52 fontFamily: "Helvetica Neue"
53 }
54});
همچنین در همین پوشه فایلی به نام shared.js با محتوای زیر ایجاد میکنیم:
1import React from 'react';
2import {View, Text, StyleSheet} from 'react-native';
3import {Icon, Badge} from 'react-native-elements';
4
5//HEADER COMPONENT
6export const Header = (props) => {
7 let {title, style} = props;
8
9 return (
10 <View style={[styles.header, style]}>
11 <Text style={styles.headerText}>
12 {title}
13 </Text>
14 </View>
15 )
16};
17
18Header.defaultProps = {
19 title: "",
20 style: {}
21};
22
23//ERROR COMPONENT
24export const ErrorText = ({error}) => {
25 return <Text style={styles.errorText}>{error}</Text>
26};
27
28ErrorText.defaultProps = {
29 error: ""
30};
31
32const styles = StyleSheet.create({
33 header: {
34 height: 50,
35 justifyContent: "center"
36 },
37
38 headerText: {
39 fontSize: 25,
40 color: "#362068",
41 fontWeight: "400",
42 fontFamily: "Helvetica Neue"
43 },
44
45 errorText:{
46 marginBottom: 8,
47 color:"red"
48 }
49});
در فایل فوق 2 کامپوننت ایجاد شدهاند که یکی Header است که برای نمایش هدر در صحنه استفاده میشود و دیگری ErrorText است که برای نمایش خطاها به کار میآید.
گام 9: صحنهها
در پوشه scenes دو پوشه به نامهای auth و home ایجاد میکنیم. در پوشه auth فایلهای زیر را ایجاد خواهیم کرد.
- فایل AuthLoading.js
1import React, {useEffect} from 'react';
2import {ActivityIndicator, View, Text} from 'react-native';
3import { StackActions } from 'react-navigation';
4
5import { useAuth } from "../../providers/auth";
6
7export default function AuthLoading(props) {
8 const {navigate} = props.navigation;
9 const { getAuthState } = useAuth();
10
11 useEffect(() => {
12 initialize()
13 }, []);
14
15 async function initialize() {
16 try {
17 const {user} = await getAuthState();
18
19 if (user) {
20 //check if username exist
21 let username = !!(user.username);
22
23 if (username) navigate('App');
24 else navigate('Auth', {}, StackActions.replace({ routeName: "Username" }))
25
26 } else navigate('Auth');
27 } catch (e) {
28 navigate('Auth');
29 }
30 }
31
32 return (
33 <View style={{backgroundColor: "#fff", alignItems: 'center', justifyContent: 'center', flex: 1}}>
34 <ActivityIndicator/>
35 <Text>{"Loading User Data"}</Text>
36 </View>
37 );
38};
این فایل در زمانی که اپلیکیشن بررسی میکند آیا کاربری وارد شده یا نه نمایش مییابد. در این کد از شیء useAuth برای بازیابی تابع getAuthState استفاده شده است. اگر کاربر موجود باشد، بررسی میکند آیا username تعیین شده یا نه و اگر چنین باشد به صحنه Home میرود، در غیر این صورت به صحنه Username خواهد رفت.
- فایل Register.js
1import React, { useState } from 'react';
2import {Alert, View} from 'react-native';
3
4import * as api from "../../services/auth";
5
6import Form from 'react-native-basic-form';
7import CTA from "../../components/CTA";
8import {Header, ErrorText} from "../../components/Shared";
9
10export default function Register(props) {
11 const {navigation} = props;
12
13 //1 - DECLARE VARIABLES
14 const [error, setError] = useState(null);
15 const [loading, setLoading] = useState(false);
16
17 const fields = [
18 {name: 'firstName', label: 'First Name', required: true},
19 {name: 'lastName', label: 'Last Name', required: true},
20 {name: 'email', label: 'Email Address', required: true},
21 {name: 'password', label: 'Password', required: true, secure:true}
22 ];
23
24 async function onSubmit(state) {
25 setLoading(true);
26
27 try {
28 let response = await api.register(state);
29 setLoading(false);
30 Alert.alert(
31 'Registration Successful',
32 response.message,
33 [{text: 'OK', onPress: () => navigation.replace("Login")}],
34 {cancelable: false},
35 );
36 } catch (error) {
37 setError(error.message);
38 setLoading(false)
39 }
40 }
41
42 let formProps = {title: "Register", fields, onSubmit, loading };
43 return (
44 <View style={{flex: 1, paddingHorizontal: 16, backgroundColor:"#fff"}}>
45 <Header title={"Register"}/>
46 <View style={{flex:1}}>
47 <ErrorText error={error}/>
48 <Form {...formProps}>
49 <CTA
50 title={"Already have an account?"}
51 ctaText={"Login"}
52 onPress={() => navigation.replace("Login")}
53 style={{marginTop: 50}}/>
54 </Form>
55 </View>
56 </View>
57 );
58};
59
60Register.navigationOptions = ({}) => {
61 return {
62 title: ``
63 }
64};
صحنه Register تابع register سرویس auth را برای ثبت نام کاربر جدید فرا میخواند. در صورت موفقیت اپلیکیشن صحنه جاری را با صحنه Login عوض میکند. به این منظور از کامپوننت CTA استفاده میشود.
- فایل Login.js
1import React, { useState } from 'react';
2import {View} from 'react-native';
3
4import * as api from "../../services/auth";
5import { useAuth } from "../../providers/auth";
6
7import Form from 'react-native-basic-form';
8import CTA from "../../components/CTA";
9import {Header, ErrorText} from "../../components/Shared";
10
11export default function Login(props) {
12 const {navigation} = props;
13 const {navigate} = navigation;
14
15 //1 - DECLARE VARIABLES
16 const [error, setError] = useState(null);
17 const [loading, setLoading] = useState(false);
18 const { handleLogin } = useAuth();
19
20 const fields = [
21 {name: 'email', label: 'Email Address', required: true},
22 {name: 'password', label: 'Password', required: true, secure: true}
23 ];
24
25 async function onSubmit(state) {
26 setLoading(true);
27
28 try {
29 let response = await api.login(state);
30 await handleLogin(response);
31
32 setLoading(false);
33
34 //check if username is null
35 let username = (response.user.username !== null);
36 if (username) navigate('App');
37 else navigation.replace('Username');
38 } catch (error) {
39 setError(error.message);
40 setLoading(false)
41 }
42 }
43
44 let formProps = {title: "Login", fields, onSubmit, loading};
45 return (
46 <View style={{flex: 1, paddingHorizontal: 16, backgroundColor:"#fff"}}>
47 <Header title={"Login"}/>
48 <View style={{flex: 1}}>
49 <ErrorText error={error}/>
50 <Form {...formProps}>
51 <CTA
52 ctaText={"Forgot Password?"}
53 onPress={() => navigation.navigate("ForgotPassword")}
54 style={{marginTop: 20}}/>
55
56 <CTA
57 title={"Don't have an account?"}
58 ctaText={"Register"}
59 onPress={() => navigation.replace("Register")}
60 style={{marginTop: 50}}/>
61 </Form>
62 </View>
63 </View>
64 );
65};
66
67Login.navigationOptions = ({}) => {
68 return {
69 title: ``
70 }
71};
صحنه Login تابع updateProfile سرویس auth را برای وارد کردن فرد به حساب کاربری خود فرا میخواند. در صورت موفقیت به صفحه Home میرود. از کامپوننت CTA استفاده میکند.
- فایل Username.js
1import React, { useState } from 'react';
2import {View} from 'react-native';
3
4import * as api from "../../services/auth";
5import { useAuth } from "../../providers/auth";
6
7import Form from 'react-native-basic-form';
8import {Header, ErrorText} from "../../components/Shared";
9
10export default function Username (props) {
11 const {navigation} = props;
12
13 //1 - DECLARE VARIABLES
14 const [error, setError] = useState(null);
15 const [loading, setLoading] = useState(false);
16 const { state, updateUser } = useAuth();
17
18 const fields = [
19 {name: 'username', label: 'Username', required: true}
20 ];
21
22 async function onSubmit(data) {
23 setLoading(true);
24
25 try {
26 let response = await api.updateProfile(state.user._id, data);
27 updateUser(response.user);
28
29 setLoading(false);
30
31 navigation.navigate('App');
32 } catch (error) {
33 setError(error.message);
34 setLoading(false)
35 }
36 }
37
38 let formProps = {title: "Submit", fields, onSubmit, loading };
39 return (
40 <View style={{flex: 1, paddingHorizontal: 16, backgroundColor:"#fff"}}>
41 <Header title={"Select Username"}/>
42 <View style={{flex:1}}>
43 <ErrorText error={error}/>
44 <Form {...formProps}/>
45 </View>
46 </View>
47 );
48};
49
50Username.navigationOptions = ({}) => {
51 return {
52 title: ``
53 }
54};
صحنه Username برای بهروزرسانی دادههای کاربران از تابع updateProfile سرویس auth استفاده میکند. در صورت موفقیت به صفحه Home میرود.
- فایل ForgotPassword.js
1import React, { useState } from 'react';
2import {Alert, View} from 'react-native';
3
4import * as api from "../../services/auth";
5
6import Form from 'react-native-basic-form';
7import {Header, ErrorText} from "../../components/Shared";
8
9export default function ForgotPassword(props) {
10 const {navigation} = props;
11
12 //1 - DECLARE VARIABLES
13 const [error, setError] = useState(null);
14 const [loading, setLoading] = useState(false);
15
16 const fields = [{name: 'email', label: 'Email Address', required: true}];
17
18 async function onSubmit(state) {
19 setLoading(true);
20
21 try {
22 let response = await api.forgotPassword(state);
23 setLoading(false);
24
25 Alert.alert(
26 'Recover Password',
27 response.message,
28 [{text: 'OK', onPress: () => navigation.goBack()}],
29 {cancelable: false},
30 );
31 } catch (error) {
32 setError(error.message);
33 setLoading(false)
34 }
35 }
36
37 let formProps = {title: "Submit", fields, onSubmit, loading };
38 return (
39 <View style={{flex: 1, paddingHorizontal: 16, backgroundColor:"#fff"}}>
40 <Header title={"Recover Password"}/>
41 <View style={{flex:1}}>
42 <ErrorText error={error}/>
43 <Form {...formProps}/>
44 </View>
45 </View>
46 );
47};
48
49ForgotPassword.navigationOptions = ({}) => {
50 return {
51 title: ``
52 }
53};
صحنه Forgot Password تابع forgotPassword سرویس auth را برای بهروزرسانی دادههای کاربران فرا میخواند. در صورت موفقیت به صفحه Login بازمیگردد. در ادامه در پوشه home دو فایل به نامهای Home.js و UpdateProfile.js ایجاد میکنیم.
- فایل Home.js
1import React, {useState, useContext} from 'react';
2import {Text, View, Button, ActivityIndicator, Alert} from 'react-native';
3
4import { useAuth } from "../../providers/auth";
5
6export default function Home(props) {
7 const {navigate} = props.navigation;
8
9 const {state, handleLogout} = useAuth();
10 const user = state.user;
11
12 return (
13 <View style={{flex: 1, backgroundColor: '#fff', alignItems: 'center', justifyContent: 'center'}}>
14 <Text>{`Welcome ${user.firstName} ${user.lastName} (${user.username})`}</Text>
15
16 <Button title={"Update Profile"} onPress={() => navigate('UpdateProfile')}/>
17
18 <Button title={"Log Out"} onPress={() => {
19 handleLogout();
20 navigate('Auth');
21 }}/>
22 </View>
23 );
24}
این فایل صفحهای با پیام خوشامدگویی و دکمههای Logout و Update Profile نمایش میدهد و از شیء useAuth برای بازیابی دادههای کاربران و تابع استفاده میکند.
- فایل UpdateProfile.js
1import React, { useState } from 'react';
2import {View} from 'react-native';
3
4import * as api from "../../services/auth";
5import { useAuth } from "../../providers/auth";
6
7import Form from 'react-native-basic-form';
8import {ErrorText} from "../../components/Shared";
9
10export default function UpdateProfile (props) {
11 const {navigation} = props;
12
13 //1 - DECLARE VARIABLES
14 const [error, setError] = useState(null);
15 const [loading, setLoading] = useState(false);
16 const { state, updateUser } = useAuth();
17
18 const fields = [
19 {name: 'firstName', label: 'First Name', required: true},
20 {name: 'lastName', label: 'Last Name', required: true},
21 {name: 'username', label: 'Username', required: true}
22 ];
23
24 async function onSubmit(data) {
25 setLoading(true);
26
27 try {
28 let response = await api.updateProfile(state.user._id, data);
29 updateUser(response.user);
30
31 setLoading(false);
32
33 navigation.goBack();
34 } catch (error) {
35 setError(error.message);
36 setLoading(false)
37 }
38 }
39
40 return (
41 <View style={{flex: 1, paddingHorizontal: 16, backgroundColor:"#fff"}}>
42 <View style={{flex:1}}>
43 <ErrorText error={error}/>
44 <Form
45 fields={fields}
46 title={'Submit'}
47 loading={loading}
48 initialData={state.user}
49 error={error}
50 onSubmit={onSubmit}/>
51 </View>
52 </View>
53 );
54};
55
56UpdateProfile.navigationOptions = ({}) => {
57 return {
58 title: `Update Profile`
59 }
60};
این فایل تابع updateProfile سرویس auth را فرا میخواند تا دادههای کاربران را بهروزرسانی کند. در صورت موفقیت اپلیکیشن به صحنه Home بازمیگردد.
گام 10: مسیرها
در پوشه routes دو فایل به نامهای auth.js و home.js ایجاد میکنیم. در این فایلها، تابع createStackNavigator مربوط به React Navigation برای ایجاد رشته مسیرها مورد استفاده قرار میگیرد. پشته مسیرهای احراز هویت شامل صحنههای Register، Login، Username و ForgotPassword است.
- فایل auth.js
1import React from 'react';
2import {createStackNavigator} from 'react-navigation-stack';
3
4//IMPORT SCENES
5import RegisterScreen from "../scenes/auth/Register";
6import LoginScreen from "../scenes/auth/Login";
7import UsernameScreen from "../scenes/auth/Username";
8import ForgotPasswordScreen from "../scenes/auth/ForgotPassword";
9
10import {headerStyle, headerTitleStyle} from '../theme'
11
12//Create Routes
13const AuthStack = createStackNavigator(
14 {
15 Register: RegisterScreen,
16 Login: LoginScreen,
17 Username: UsernameScreen,
18 ForgotPassword: ForgotPasswordScreen
19 },
20 {
21 initialRouteName: 'Login',
22 defaultNavigationOptions: () => ({headerStyle, headerTitleStyle})
23 }
24);
25
26export default AuthStack;
- فایل home.js
1import React from 'react';
2import {createStackNavigator} from 'react-navigation-stack';
3
4//IMPORT SCENES
5import HomeScreen from "../scenes/home/Home";
6import UpdateProfileScreen from "../scenes/home/UpdateProfile";
7
8import {headerStyle, headerTitleStyle} from '../theme'
9
10const HomeStack = createStackNavigator(
11 {
12 Home: HomeScreen,
13 UpdateProfile: UpdateProfileScreen,
14 },
15 {
16 initialRouteName: 'Home',
17 defaultNavigationOptions: () => ({headerStyle, headerTitleStyle})
18 }
19);
20
21export default HomeStack;
بخش چهارم
در این بخش به جمعبندی همه مباحث مطرحشده میپردازیم.
روتر
در پوشه app یک فایل به نام router.js و با محتوای زیر ایجاد میکنیم:
1import React from 'react';
2
3import {createAppContainer, createSwitchNavigator} from 'react-navigation';
4
5//IMPORT ROUTES
6import AuthStack from "./routes/auth";
7import HomeStack from "./routes/home";
8
9import AuthLoading from "./scenes/auth/AuthLoading";
10import AuthProvider from "./providers/auth";
11
12//APP ROUTES STACK
13const AppStack = createSwitchNavigator(
14 {
15 Loading: AuthLoading,
16 Auth: AuthStack,
17 App: HomeStack
18 },
19 {initialRouteName: 'Loading'}
20);
21
22const Navigator = createAppContainer(AppStack);
23
24export default function Router(props) {
25 return (
26 <AuthProvider>
27 <Navigator/>
28 </AuthProvider>
29 );
30}
در این فایل با استفاده از تابع createSwitchNavigator پشته مسیرهای اپلیکیشن شامل صحنه AuthLoading، پشته مسیرهای احراز هویت و پشته home ایجاد میشود. Provider ایمپورت شده و پشته مسیرهای اپلیکیشن درون آن قرار میگیرد تا مقدار context در همه صحنهها در اختیار ما قرار داشته باشد.
در ادامه فایل App.js را به عنوان مدخل ورودی اصلی اپلیکیشن با محتوای زیر ایجاد میکنیم:
1// * Description: App Entry Point
2import React, {Component} from 'react';
3
4import Router from './app/router'
5
6export default class App extends Component {
7 render() {
8 return <Router/>;
9 }
10}
در فایل فوق روتر ایمپورت شده است.
گام 11: تست
برای تست اپلیکیشن دستور زیر را اجرا کنید:
Expo $ expo start React Native CLI $ npx react-native run-ios
بدین ترتیب به پایان این راهنما میرسیم.
اگر این مطلب برای شما مفید بوده است، آموزشهای زیر نیز به شما پیشنهاد میشوند:
- مجموعه آموزشهای JavaScript (جاوا اسکریپت)
- آموزش JavaScript ES6 (جاوا اسکریپت)
- مجموعه آموزشهای برنامهنویسی
- آموزش جاوا اسکریپت — مجموعه مقالات جامع وبلاگ فرادرس
- افزودن صفحه آغازین (Splash Screen) به اپ React Native — از صفر تا صد
==
سلام
ممنون مطلب خوبی بود …
فقط اینکه بنظر میرسه فایل auth.js که در provider قرار داره ، ناقصه چونکه با توضیحاتی که زیرش نوشتین مطابقت نداره.
سلام دوست عزیز؛
موردی که فرمودید بررسی شد. بخشی از کد به درستی درج نشده بود که اصلاح شد.
از توجه و دقت نظر شما سپاسگزاریم.