共计 20184 个字符,预计需要花费 51 分钟才能阅读完成。
翻译 |《JavaScript Everywhere》第 24 章 挪动应用程序认证
写在最后面
大家好呀,我是毛小悠,是一位前端开发工程师。正在翻译一本英文技术书籍。
为了进步大家的浏览体验,对语句的构造和内容略有调整。如果发现本文中有存在瑕疵的中央,或者你有任何意见或者倡议,能够在评论区留言,或者加我的微信:code_maomao,欢送互相沟通交流学习。
(σ゚∀゚)σ..:*☆哎哟不错哦
第 24 章 挪动应用程序认证
如果你已经与亲戚同住过,或者在度假的旅馆里或租了带家具的公寓,那么你就会体验到被不属于你的事物突围着是什么样的感触。
在这些类型的环境中可能很难感到安宁,不想将某些货色放到适当的地位或弄乱它们。当我处于这些状况下时,无论房东如许友善或容纳,这种所有权的不足使我处于焦躁的边缘状态。我能说什么,我只是不难受,除非我能够不必杯垫放玻璃杯。
因为无奈自定义或读取特定于用户的数据,咱们的利用可能会给用户带来同样的不适感。他们的笔记与其余所有人的笔记混在一起,而不是使应用程序真正属于本人。在本章中,咱们将向咱们的应用程序增加身份验证。为此,咱们将介绍一个身份验证路由流,应用 Expo
的SecureStore
存储令牌数据,在 React Native
中创立文本表单以及执行身份验证 GraphQL
申请。
认证路由流程
让咱们从创立身份验证流程开始。当用户首次拜访咱们的应用程序时,咱们将为他们显示登录界面。当用户登录时,咱们会将令牌存储在设施上,从而容许他们绕过未来应用程序应用时的登录界面。咱们还将增加一个设置界面,用户能够在其中单击按钮以退出应用程序,并从其设施中删除令牌。
为此,咱们将增加几个新界面:
authloading.js
这将是一个插页式界面,用户将不会与之互动。
关上应用程序后,咱们将应用界面查看令牌是否存在,并将用户导航到登录界面或应用程序内容。
signin.js
这是用户能够登录其帐户的界面。
胜利登录后,咱们将令牌存储在设施上。
settings.js
在设置界面中,用户将可能单击按钮并登记该应用程序。
一旦登记,它们将被路由回登录界面。
应用现有帐户
在本章的前面,咱们将增加通过该应用程序创立帐户的性能。如果还没有,间接通过你的 API
实例的 GraphQL Playground
或Web
利用程序界面创立一个帐户将很有用。
为了存储和应用令牌,咱们将应用 Expo
的SecureStore
库。
我发现 SecureStore
是在设施上本地加密和存储数据的间接办法。对于 iOS
设施,SecureStore
应用内置的钥匙串服务,而在 Android
上,它应用操作系统的“共享首选项”,并应用 Keystore
加密数据。
所有这些都是在后盾进行的,这使咱们可能简略地存储和检索数据。
首先,咱们将创立登录界面。目前,咱们的登录界面将蕴含一个 Button
组件,当按下该按钮时,它将存储令牌。让咱们在 src/screens/signin.js
中创立一个新的界面组件,导入依赖项:
import React from 'react';
import {View, Button, Text} from 'react-native';
import * as SecureStore from 'expo-secure-store';
const SignIn = props => {
return (<View> <Button title="Sign in!" /> </View>);
}
SignIn.navigationOptions = {title: 'Sign In'};
export default SignIn;
接下来,让咱们在 src/screens/authloading.js
中创立咱们的身份验证加载组件,该组件当初仅显示一个加载指示器:
import React, {useEffect} from 'react';
import * as SecureStore from 'expo-secure-store';
import Loading from '../components/Loading';
const AuthLoading = props => {return <Loading />;};
export default AuthLoading;
最初,咱们能够在 src/screens/settings.js
中创立设置界面:
import React from 'react';
import {View, Button} from 'react-native';
import * as SecureStore from 'expo-secure-store';
const Settings = props => {
return (<View> <Button title="Sign Out" /> </View>);
};
Settings.navigationOptions = {title: 'Settings'};
export default Settings;
编写完这些组件后,咱们将更新路由以解决通过身份验证和未经身份验证的状态。在 src/screens/index.js
中,将新界面增加到咱们的 import
语句列表中,如下所示:
import AuthLoading from './authloading';
import SignIn from './signin';
import Settings from './settings';
咱们还须要更新咱们的 react-navigation
依赖项,以包含createSwitchNavigator
,这使咱们能够一次显示一个界面并在它们之间切换。
当用户导航并且不提供向后导航选项时,SwitchNavigator
会将路由重置为默认状态。
import {createAppContainer, createSwitchNavigator} from 'react-navigation';
咱们能够为咱们的身份验证和设置界面创立一个新的StackNavigator
。
这将使咱们可能在未来或须要时增加子导航界面:
const AuthStack = createStackNavigator({SignIn: SignIn});
const SettingsStack = createStackNavigator({Settings: Settings});
而后,咱们将设置界面增加到底部的TabNavigator
。
选项卡的其余导航设置将放弃不变:
const TabNavigator = createBottomTabNavigator({
FeedScreen: {// ...},
MyNoteScreen: {// ...},
FavoriteScreen: {// ...},
Settings: {
screen: Settings,
navigationOptions: {
tabBarLabel: 'Settings',
tabBarIcon: ({tintColor}) => (<MaterialCommunityIcons name="settings" size={24} color={tintColor} /> )
}
}
});
当初,咱们能够通过定义要切换的界面并设置默认界面 AuthLoading
来创立 SwitchNavigator
。而后,咱们将现有的导出语句替换为导出SwitchNavigator
的语句:
const SwitchNavigator = createSwitchNavigator(
{
AuthLoading: AuthLoading,
Auth: AuthStack,
App: TabNavigator
},
{initialRouteName: 'AuthLoading'}
);
export default createAppContainer(SwitchNavigator);
总之,咱们的 src/screens/index.js
文件将显示如下:
import React from 'react';
import {Text, View, ScrollView, Button} from 'react-native';
import {createAppContainer, createSwitchNavigator} from 'react-navigation';
import {createBottomTabNavigator} from 'react-navigation-tabs';
import {createStackNavigator} from 'react-navigation-stack';
import {MaterialCommunityIcons} from '@expo/vector-icons';
import Feed from './feed';
import Favorites from './favorites';
import MyNotes from './mynotes';
import Note from './note';
import SignIn from './signin';
import AuthLoading from './authloading';
import Settings from './settings';
const AuthStack = createStackNavigator({SignIn: SignIn,});
const FeedStack = createStackNavigator({
Feed: Feed,
Note: Note
});
const MyStack = createStackNavigator({
MyNotes: MyNotes,
Note: Note
});
const FavStack = createStackNavigator({
Favorites: Favorites,
Note: Note
});
const SettingsStack = createStackNavigator({Settings: Settings});
const TabNavigator = createBottomTabNavigator({
FeedScreen: {
screen: FeedStack,
navigationOptions: {
tabBarLabel: 'Feed',
tabBarIcon: ({tintColor}) => (<MaterialCommunityIcons name="home" size={24} color={tintColor} />
)
}
},
MyNoteScreen: {
screen: MyStack,
navigationOptions: {
tabBarLabel: 'My Notes',
tabBarIcon: ({tintColor}) => (<MaterialCommunityIcons name="notebook" size={24} color={tintColor} />
)
}
},
FavoriteScreen: {
screen: FavStack,
navigationOptions: {
tabBarLabel: 'Favorites',
tabBarIcon: ({tintColor}) => (<MaterialCommunityIcons name="star" size={24} color={tintColor} />
)
}
},
Settings: {
screen: SettingsStack,
navigationOptions: {
tabBarLabel: 'Settings',
tabBarIcon: ({tintColor}) => (<MaterialCommunityIcons name="settings" size={24} color={tintColor} />
)
}
}
});
const SwitchNavigator = createSwitchNavigator(
{
AuthLoading: AuthLoading,
Auth: AuthStack,
App: TabNavigator
},
{initialRouteName: 'AuthLoading'}
);
export default createAppContainer(SwitchNavigator);
当初,当咱们浏览应用程序时,因为 AuthLoading
路由是初始界面,因而咱们只会看到正在加载界面。让咱们对其进行更新,以便在加载界面时查看应用程序的 SecureStore
中是否存在令牌值。如果令牌存在,咱们将疏导用户到主利用程序界面。然而,如果不存在令牌,则应将用户路由到登录界面。让咱们更新 src/screens/authloading.js
来执行此查看:
import React, {useEffect} from 'react';
import * as SecureStore from 'expo-secure-store';
import Loading from '../components/Loading';
const AuthLoadingScreen = props => {const checkLoginState = async () => {
// retrieve the value of the token
const userToken = await SecureStore.getItemAsync('token');
// navigate to the app screen if a token is present
// else navigate to the auth screen
props.navigation.navigate(userToken ? 'App' : 'Auth');
};
// call checkLoginState as soon as the component mounts
useEffect(() => {checkLoginState();
});
return <Loading />;
};
export default AuthLoadingScreen;
进行此更改后,因为没有令牌,因而当咱们加载应用程序时,咱们当初应该被路由到登录界面。当初,让咱们更新登录界面以存储通用令牌,并在用户按下按钮时导航到应用程序(图24-1
):
import React from 'react';
import {View, Button, Text} from 'react-native';
import * as SecureStore from 'expo-secure-store';
const SignIn = props => {
// store the token with a key value of `token`
// after the token is stored navigate to the app's main screen
const storeToken = () => {SecureStore.setItemAsync('token', 'abc').then(props.navigation.navigate('App')
);
};
return (<View> <Button title="Sign in!" onPress={storeToken} /> </View> );
};
SignIn.navigationOptions = {title: 'Sign In'};
export default SignIn;
图24-1
。单击该按钮将存储令牌并将用户路由到应用程序
当初,当用户按下按钮时,将通过 SecureStore
存储令牌。应用登录性能后,让咱们为用户增加退出应用程序的性能。为此,咱们将在设置界面上增加一个按钮,当按下该按钮时,将从 SecureStore
中删除令牌(图 24-2
)。在src/screens/settings.js
中:
import React from 'react';
import {View, Button} from 'react-native';
import * as SecureStore from 'expo-secure-store';
const Settings = props => {
// delete the token then navigate to the auth screen
const signOut = () => {SecureStore.deleteItemAsync('token').then(props.navigation.navigate('Auth')
);
};
return (<View> <Button title="Sign Out" onPress={signOut} /> </View> );
};
Settings.navigationOptions = {title: 'Settings'};
export default Settings;
图24-2
。单击该按钮将从设施中删除令牌,并使用户返回登录界面
实现这些步骤后,咱们便领有了创立应用程序身份验证流程所需的所有。
请确保退出
如果还没有,请在本地应用程序实例中点击“退出”按钮。
咱们将在接下来的局部中增加适当的登录性能。
创立登录表单
当初,咱们能够单击一个按钮并将令牌存储在用户的设施上,但咱们还不容许用户通过输出本人的信息来登录帐户。让咱们通过创立一个表单来纠正此问题,用户能够在其中输出他们的电子邮件地址和明码。为此,咱们将应用 React Native
的TextInput
组件在 src/components/UserForm.js
中应用表单创立一个新组件:
import React, {useState} from 'react';
import {View, Text, TextInput, Button, TouchableOpacity} from 'react-native';
import styled from 'styled-components/native';
const UserForm = props => {
return (<View> <Text>Email</Text> <TextInput /> <Text>Password</Text> <TextInput /> <Button title="Log In" /> </View>);
}
export default UserForm;
当初,咱们能够在身份验证界面上显示此表单。为此,更新 src/screens/signin.js
以导入和应用组件,如下所示:
import React from 'react';
import {View, Button, Text} from 'react-native';
import * as SecureStore from 'expo-secure-store';
import UserForm from '../components/UserForm';
const SignIn = props => {const storeToken = () => {SecureStore.setItemAsync('token', 'abc').then(props.navigation.navigate('App')
);
};
return (<View> <UserForm /> </View>);
}
export default SignIn;
这样,咱们将在身份验证界面上看到一个根本表单,然而它短少任何款式或性能。咱们能够持续在 src/components/UserForm.js
文件中实现表单。咱们将应用 React
的useState
挂钩,用于读取和设置表单元素的值:
const UserForm = props => {
// form element state
const [email, setEmail] = useState();
const [password, setPassword] = useState();
return (
<View>
<Text>Email</Text>
<TextInput onChangeText={text => setEmail(text)} value={email} />
<Text>Password</Text>
<TextInput onChangeText={text => setPassword(text)} value={password} />
<Button title="Log In" />
</View>
);
}
当初,咱们能够在表单元素中增加一些其余属性,在应用电子邮件地址或明码时为用户提供预期的性能。无关 TextInput API
的残缺文档,请参见
React
本地文档。按下按钮时,咱们还会调用一个函数,只管该性能会受到限制。
const UserForm = props => {
// form element state
const [email, setEmail] = useState();
const [password, setPassword] = useState();
const handleSubmit = () => {// this function is called when the user presses the form button};
return (
<View>
<Text>Email</Text>
<TextInput
onChangeText={text => setEmail(text)}
value={email}
textContentType="emailAddress"
autoCompleteType="email"
autoFocus={true}
autoCapitalize="none"
/>
<Text>Password</Text>
<TextInput
onChangeText={text => setPassword(text)}
value={password}
textContentType="password"
secureTextEntry={true}
/>
<Button title="Log In" onPress={handleSubmit} />
</View>
);
}
咱们的表单具备所有必要的组件,然而款式还有很多不足之处。让咱们应用款式化组件库为表单提供更适合的外观:
import React, {useState} from 'react';
import {View, Text, TextInput, Button, TouchableOpacity} from 'react-native';
import styled from 'styled-components/native';
const FormView = styled.View` padding: 10px; `;
const StyledInput = styled.TextInput` border: 1px solid gray;
font-size: 18px;
padding: 8px;
margin-bottom: 24px; `;
const FormLabel = styled.Text` font-size: 18px;
font-weight: bold; `;
const UserForm = props => {const [email, setEmail] = useState();
const [password, setPassword] = useState();
const handleSubmit = () => {// this function is called when the user presses the form button};
return (
<FormView>
<FormLabel>Email</FormLabel>
<StyledInput
onChangeText={text => setEmail(text)}
value={email}
textContentType="emailAddress"
autoCompleteType="email"
autoFocus={true}
autoCapitalize="none"
/>
<FormLabel>Password</FormLabel>
<StyledInput
onChangeText={text => setPassword(text)}
value={password}
textContentType="password"
secureTextEntry={true}
/>
<Button title="Log In" onPress={handleSubmit} />
</FormView>
);
};
export default UserForm;
最初,咱们的 Button
组件仅限于默认款式选项,但承受色彩属性值除外。要创立自定义款式的按钮组件,咱们能够应用 React Native
包装器TouchableOpacity
(参见图24-3
):
const FormButton = styled.TouchableOpacity`
background: #0077cc;
width: 100%;
padding: 8px;
`;
const ButtonText = styled.Text`
text-align: center;
color: #fff;
font-weight: bold;
font-size: 18px;
`;
const UserForm = props => {const [email, setEmail] = useState();
const [password, setPassword] = useState();
const handleSubmit = () => {// this function is called when the user presses the form button};
return (
<FormView>
<FormLabel>Email</FormLabel>
<StyledInput
onChangeText={text => setEmail(text)}
value={email}
textContentType="emailAddress"
autoCompleteType="email"
autoFocus={true}
autoCapitalize="none"
/>
<FormLabel>Password</FormLabel>
<StyledInput
onChangeText={text => setPassword(text)}
value={password}
textContentType="password"
secureTextEntry={true}
/>
<FormButton onPress={handleSubmit}>
<ButtonText>Submit</ButtonText>
</FormButton>
</FormView>
);
};
这样,咱们实现了登录表单并利用了自定义款式。当初,让咱们实现表单的性能。
图24-3
。咱们的登录表单具备自定义款式
应用 GraphQL 批改进行身份验证
你可能还记得咱们在 API
和Web
应用程序章节开发的身份验证流程,然而在持续进行之前,让咱们疾速回顾一下。咱们会将 GraphQL
申请发送到咱们的 API
,其中包含用户的电子邮件地址和明码。如果数据库中存在电子邮件地址,并且明码正确,咱们的API
将以 JWT
进行响应。而后,咱们能够像以前一样将令牌存储在用户的设施上,并将其与每个 GraphQL
申请一起发送。这样一来,咱们就能够在每个 API
申请中辨认用户,而无需他们从新输出明码。
搁置好表格后,咱们能够在 src/screens/signin.js
中编写 GraphQL
申请。首先,咱们将 Apollo
库以及咱们的 Loading
组件增加到导入列表中:
import React from 'react';
import {View, Button, Text} from 'react-native';
import * as SecureStore from 'expo-secure-store';
import {useMutation, gql} from '@apollo/client';
import UserForm from '../components/UserForm';
import Loading from '../components/Loading';
接下来,咱们能够增加咱们的 GraphQL
查问:
const SIGNIN_USER = gql`
mutation signIn($email: String, $password: String!) {signIn(email: $email, password: $password)
}
`;
更新咱们的 storeToken
函数存储作为参数传递的令牌字符串:
const storeToken = token => {SecureStore.setItemAsync('token', token).then(props.navigation.navigate('App')
);
};
最初,咱们将组件更新为 GraphQL
批改。咱们还将把几个属性值传递给 UserForm
组件,使咱们能够共享批改数据,辨认正在调用的表单的类型以及利用路由器的导航。
const SignIn = props => {
const storeToken = token => {SecureStore.setItemAsync('token', token).then(props.navigation.navigate('App')
);
};
const [signIn, { loading, error}] = useMutation(SIGNIN_USER, {
onCompleted: data => {storeToken(data.signIn)
}
});
// if loading, return a loading indicator
if (loading) return <Loading />;
return (
<React.Fragment>
{error && <Text>Error signing in!</Text>}
<UserForm
action={signIn}
formType="signIn"
navigation={props.navigation}
/>
</React.Fragment>
);
};
当初,咱们能够在 src/components/UserForm.js
组件中进行简略的更改,这将使其可能将用户输出的数据传递给申请。在组件内,咱们将更新 handleSubmit
函数,以将表单值传递给咱们的申请:
const handleSubmit = () => {
props.action({
variables: {
email: email,
password: password
}
});
};
编写好咱们的申请并实现表格后,用户当初能够登录该应用程序,该应用程序将存储返回的 JSON Web
令牌以供未来应用。
认证的 GraphQL 查问
当初咱们的用户能够登录其帐户了,咱们将须要应用存储的令牌对每个申请进行身份验证。这将使咱们可能申请特定于用户的数据,例如以后用户的笔记列表或用户已标记为“收藏夹”的笔记列表。为此,咱们将更新 Apollo
配置以查看令牌是否存在,如果令牌存在,则在每个 API
调用中发送该令牌的值。
在 src/Main.js
中,首先将 SecureStore
增加到导入列表中,并更新 Apollo Client
依赖项以包含 createHttpLink
和setContext
:
// import the Apollo libraries
import {
ApolloClient,
ApolloProvider,
createHttpLink,
InMemoryCache
} from '@apollo/client';
import {setContext} from 'apollo-link-context';
// import SecureStore for retrieving the token value
import * as SecureStore from 'expo-secure-store';
而后,咱们能够更新 Apollo
客户端配置,以随每个申请发送令牌值:
// configure our API URI & cache
const uri = API_URI;
const cache = new InMemoryCache();
const httpLink = createHttpLink({uri});
// return the headers to the context
const authLink = setContext(async (_, { headers}) => {
return {
headers: {
...headers,
authorization: (await SecureStore.getItemAsync('token')) || ''
}
};
});
// configure Apollo Client
const client = new ApolloClient({link: authLink.concat(httpLink),
cache
});
通过在每个申请的标头中发送令牌,咱们当初能够更新 mynotes
和“收藏夹”界面以申请用户特定的数据。如果依照网络章节进行操作,这些查问应该看起来十分相熟。
在 src/screens/mynotes.js
中:
import React from 'react';
import {Text, View} from 'react-native';
import {useQuery, gql} from '@apollo/client';
import NoteFeed from '../components/NoteFeed';
import Loading from '../components/Loading';
// our GraphQL query
const GET_MY_NOTES = gql`
query me {
me {
id
username
notes {
id
createdAt
content
favoriteCount
author {
username
id
avatar
}
}
}
}
`;
const MyNotes = props => {const { loading, error, data} = useQuery(GET_MY_NOTES);
// if the data is loading, our app will display a loading message
if (loading) return <Loading />;
// if there is an error fetching the data, display an error message
if (error) return <Text>Error loading notes</Text>;
// if the query is successful and there are notes, return the feed of notes
// else if the query is successful and there aren't notes, display a message
if (data.me.notes.length !== 0) {return <NoteFeed notes={data.me.notes} navigation={props.navigation} />;
} else {return <Text>No notes yet</Text>;}
};
MyNotes.navigationOptions = {title: 'My Notes'};
export default MyNotes;
在 src/screens/favorites.js
中:
import React from 'react';
import {Text, View} from 'react-native';
import {useQuery, gql} from '@apollo/client';
import NoteFeed from '../components/NoteFeed';
import Loading from '../components/Loading';
// our GraphQL query
const GET_MY_FAVORITES = gql`
query me {
me {
id
username
favorites {
id
createdAt
content
favoriteCount
author {
username
id
avatar
}
}
}
}
`;
const Favorites = props => {const { loading, error, data} = useQuery(GET_MY_FAVORITES);
// if the data is loading, our app will display a loading message
if (loading) return <Loading />;
// if there is an error fetching the data, display an error message
if (error) return <Text>Error loading notes</Text>;
// if the query is successful and there are notes, return the feed of notes
// else if the query is successful and there aren't notes, display a message
if (data.me.favorites.length !== 0) {return <NoteFeed notes={data.me.favorites} navigation={props.navigation} />;
} else {return <Text>No notes yet</Text>;}
};
Favorites.navigationOptions = {title: 'Favorites'};
export default Favorites;
图24-4
。在每个申请的标头中传递令牌能够使咱们在应用程序中进行特定于用户的查问
当初,咱们将基于存储在用户设施上的令牌值来检索用户特定的数据(图24-4
)。
增加注册表格
当初,用户能够登录到现有帐户,然而如果不存在,则无奈创立帐户。常见的 UI
模式是在登录链接下方增加指向注册表单的链接(反之亦然)。让咱们增加一个注册界面,以容许用户从咱们的应用程序中创立一个新帐户。
首先,让咱们在 src/screens/signup.js
中创立一个新的界面组件。该组件与登录界面简直雷同,然而咱们将其称为 signUp GraphQL
申请,并将 formType
=“signUp
”属性传递给UserForm
组件:
import React from 'react';
import {Text} from 'react-native';
import * as SecureStore from 'expo-secure-store';
import {useMutation, gql} from '@apollo/client';
import UserForm from '../components/UserForm';
import Loading from '../components/Loading';
// signUp GraphQL mutation
const SIGNUP_USER = gql`
mutation signUp($email: String!, $username: String!, $password: String!) {signUp(email: $email, username: $username, password: $password)
}
`;
const SignUp = props => {
// store the token with a key value of `token`
// after the token is stored navigate to the app's main screen
const storeToken = token => {SecureStore.setItemAsync('token', token).then(props.navigation.navigate('App')
);
};
// the signUp mutation hook
const [signUp, { loading, error}] = useMutation(SIGNUP_USER, {
onCompleted: data => {storeToken(data.signUp);
}
});
// if loading, return a loading indicator
if (loading) return <Loading />;
return (
<React.Fragment>
{error && <Text>Error signing in!</Text>}
<UserForm
action={signUp}
formType="signUp"
navigation={props.navigation}
/>
</React.Fragment>
);
};
SignUp.navigationOptions = {title: 'Register'};
export default SignUp;
创立界面后,咱们能够将其增加到路由器。在 src/screens/index.js
文件中,首先将新组件增加到咱们的文件导入列表中:
import SignUp from './signup';
接下来,咱们将更新 AuthStack
包含注册界面:
const AuthStack = createStackNavigator({
SignIn: SignIn,
SignUp: SignUp
});
这样,咱们的组件就能够创立并路由了;然而,咱们的 UserForm
组件并不蕴含所有必须的字段。除了创立注册表单组件之外,依据类型,应用传递给自定义的表格的 UrserForm 的 formType 属性。
在咱们的 src/components/UserForm.js
文件中,在 formType
等于 signUp
时,让咱们更新表单的用户名字段:
const UserForm = props => {const [email, setEmail] = useState();
const [password, setPassword] = useState();
const [username, setUsername] = useState();
const handleSubmit = () => {
props.action({
variables: {
email: email,
password: password,
username: username
}
});
};
return (
<FormView>
<FormLabel>Email</FormLabel>
<StyledInput onChangeText={text => setEmail(text)}
value={email}
textContentType="emailAddress"
autoCompleteType="email"
autoFocus={true}
autoCapitalize="none"
/>
{props.formType === 'signUp' && (
<View>
<FormLabel>Username</FormLabel>
<StyledInput onChangeText={text => setUsername(text)}
value={username}
textContentType="username"
autoCapitalize="none"
/>
</View>
)}
<FormLabel>Password</FormLabel>
<StyledInput onChangeText={text => setPassword(text)}
value={password}
textContentType="password"
secureTextEntry={true}
/>
<FormButton onPress={handleSubmit}>
<ButtonText>Submit</ButtonText>
</FormButton>
</FormView>
);
};
接下来,让咱们在登录表单的底部增加一个链接,该链接使用户在按下时能够转到登录表单:
return (<FormView> {/* existing form component code is here */}
{props.formType !== 'signUp' && ( <TouchableOpacity onPress={() => props.navigation.navigate('SignUp')}> <Text>Sign up</Text> </TouchableOpacity> )} </FormView>
)
而后,咱们能够应用款式化的组件来更新链接的外观:
const SignUp = styled.TouchableOpacity`
margin-top: 20px;
`;
const Link = styled.Text`
color: #0077cc;
font-weight: bold;
`;
在组件的 JSX
中:
{props.formType !== 'signUp' && (<SignUp onPress={() => props.navigation.navigate('SignUp')}> <Text> Need an account? <Link>Sign up.</Link> </Text> </SignUp>
)}
当初,咱们的 src/components/UserForm.js
文件将如下所示:
import React, {useState} from 'react';
import {View, Text, TextInput, Button, TouchableOpacity} from 'react-native';
import styled from 'styled-components/native';
const FormView = styled.View`
padding: 10px;
`;
const StyledInput = styled.TextInput`
border: 1px solid gray;
font-size: 18px;
padding: 8px;
margin-bottom: 24px;
`;
const FormLabel = styled.Text`
font-size: 18px;
font-weight: bold;
`;
const FormButton = styled.TouchableOpacity`
background: #0077cc;
width: 100%;
padding: 8px;
`;
const ButtonText = styled.Text`
text-align: center;
color: #fff;
font-weight: bold;
font-size: 18px;
`;
const SignUp = styled.TouchableOpacity`
margin-top: 20px;
`;
const Link = styled.Text`
color: #0077cc;
font-weight: bold;
`;
const UserForm = props => {const [email, setEmail] = useState();
const [password, setPassword] = useState();
const [username, setUsername] = useState();
const handleSubmit = () => {
props.action({
variables: {
email: email,
password: password,
username: username
}
});
};
return (
<FormView>
<FormLabel>Email</FormLabel>
<StyledInput
onChangeText={text => setEmail(text)}
value={email}
textContentType="emailAddress"
autoCompleteType="email"
autoFocus={true}
autoCapitalize="none"
/>
{props.formType === 'signUp' && (
<View>
<FormLabel>Username</FormLabel>
<StyledInput
onChangeText={text => setUsername(text)}
value={username}
textContentType="username"
autoCapitalize="none"
/>
</View>
)}
<FormLabel>Password</FormLabel>
<StyledInput
onChangeText={text => setPassword(text)}
value={password}
textContentType="password"
secureTextEntry={true}
/>
<FormButton onPress={handleSubmit}>
<ButtonText>Submit</ButtonText>
</FormButton>
{props.formType !== 'signUp' && (<SignUp onPress={() => props.navigation.navigate('SignUp')}>
<Text>
Need an account? <Link>Sign up.</Link>
</Text>
</SignUp>
)}
</FormView>
);
};
export default UserForm;
进行了这些更改后,用户能够应用咱们的应用程序登录并注册帐户(图24-5
)。
图 24-5
用户当初能够注册帐户并在身份验证界面之间导航
论断
在本章中,咱们钻研了如何将身份验证引入应用程序。通过联合 React Native
的表单元素、React Navigation
的路由性能、Expo
的 SecureStore
库和 GraphQL
申请,咱们能够创立一个用户敌对的身份验证流程。对这种身份验证有扎实的理解,这也使咱们可能摸索其余 React Native
身份验证办法,例如 Expo
的AppAuth
或 GoogleSignIn
。在下一章中,咱们将钻研如何公布和散发React Native
应用程序。
如果有了解不到位的中央,欢送大家纠错。如果感觉还能够,麻烦您点赞珍藏或者分享一下,心愿能够帮到更多人。