Эта статья была рецензирована Адрианом Санду . Спасибо всем рецензентам SitePoint за то, что сделали контент SitePoint как можно лучше!
React Native — один из самых популярных вариантов создания кроссплатформенных мобильных приложений с использованием JavaScript и React.
Для многих приложений необходима регистрация и аутентификация пользователей, и в этом руководстве я собираюсь использовать Firebase для реализации аутентификации в приложении React Native.
Я тестирую на Android, но код в этом уроке должен работать и на iOS. Я предполагаю, что вы уже работали с React Native, поэтому я не буду вдаваться в подробности всего кода React Native. Если вы новичок в React Native, я рекомендую вам прочитать мой предыдущий учебник о том, как создать приложение для Android с React Native .
Вот как будет выглядеть финальное приложение:
Окончательный код на GitHub .
Создание приложения Firebase
Для работы с Firebase сначала необходимо создать приложение на Firebase. Войдите в свою панель и создайте новое приложение. Вам нужно будет изменить имя на что-то уникальное.
После создания нажмите кнопку управления приложением , затем войдите в систему и выполните авторизацию и обновите продолжительность сеанса по своему вкусу. Этот параметр позволяет изменить время, в течение которого каждый пользовательский сеанс будет оставаться действительным. Я обычно придерживаюсь 5 недель, это означает, что пользователю придется заходить каждые 5 недель.
Затем включите аутентификацию по электронной почте и паролю, которая позволяет пользователям создавать или входить в учетную запись с комбинацией электронной почты и пароля.
Сборка приложения
Приложение будет простой системой входа в систему со страницей входа, страницей регистрации и страницей учетной записи. Пользователь войдет в систему с адресом электронной почты и паролем. Если учетная запись действительна, пользователь будет перенаправлен на страницу учетной записи, где отображаются информация о пользователе и кнопка выхода из системы. Нажатие на кнопку выхода из системы разрушает сеанс Firebase, очищает локальное хранилище и возвращает пользователя на страницу входа.
Настроить
Создайте проект, выполнив следующую команду:
react-native init rnfirebaseauth
Далее установите React родной одаренной прядильщика и Firebase :
npm install react-native-gifted-spinner firebase --save
Как следует из названия, «React native одаренный прядильщик» позволяет создавать прядильщики для указания того, что приложение что-то загружает. Это приложение будет использовать спиннер во время общения с Firebase.
Структура каталогов
Создайте папку src внутри директории вашего проекта и внутри создайте папку компонентов , страниц и стилей .
Ваша структура каталогов теперь должна выглядеть так:
rnfirebaseauth android ios node_modules package.json index.android.js index.ios.js src components pages styles
Вот для чего будет каждая папка в каталоге src :
- Компоненты : содержит пользовательские компоненты, используемые приложением. Главным образом для удобства, чтобы вам не приходилось писать много кода при использовании различных компонентов пользовательского интерфейса, таких как кнопки и заголовки.
- страницы : содержит отдельные страницы приложения.
- Стили : содержит общие стили, используемые в приложении.
Компоненты
кнопка
Компонент кнопки позволяет создавать кнопки. Он использует props
для указания текста кнопки, стилей и функции, выполняемой при нажатии кнопки. Создайте компоненты / button.js и добавьте следующий код:
'use strict'; import React, { AppRegistry, Component, Text, View, TouchableHighlight } from 'react-native'; export default class button extends Component { render(){ return ( <View> <TouchableHighlight underlayColor={"#E8E8E8"} onPress={this.props.onpress} style={this.props.button_styles}> <View> <Text style={this.props.button_text_styles}>{this.props.text}</Text> </View> </TouchableHighlight> </View> ); } } AppRegistry.registerComponent('button', () => button);
заголовок
Компонент заголовка позволяет создавать заголовки. Заголовок имеет заголовок и спиннер, который показывает, когда loaded
props
имеет значение false
. В блесне используется установленный ранее одаренный блесна React. Создайте компоненты / header.js и добавьте следующий код:
'use strict'; import React, { AppRegistry, Component, StyleSheet, Text, TextInput, View } from 'react-native'; import GiftedSpinner from 'react-native-gifted-spinner'; export default class header extends Component { render(){ return ( <View style={styles.header}> <View style={styles.header_item}> <Text style={styles.header_text}>{this.props.text}</Text> </View> <View style={styles.header_item}> { !this.props.loaded && <GiftedSpinner /> } </View> </View> ); } } const styles = StyleSheet.create({ header: { padding: 10, flexDirection: 'row', alignItems: 'center', marginBottom: 20, flex: 1 }, header_item: { paddingLeft: 10, paddingRight: 10 }, header_text: { color: '#000', fontSize: 18 } }); AppRegistry.registerComponent('header', () => header);
страницы
Страница регистрации
Страница регистрации является страницей приложения по умолчанию и позволяет пользователю создать учетную запись. Создайте pages / signup.js и добавьте следующее:
'use strict'; import React, { AppRegistry, Component, Text, TextInput, View } from 'react-native'; import Button from '../components/button'; import Header from '../components/header'; import Login from './login'; import Firebase from 'firebase'; let app = new Firebase("YOUR-FIREBASE-APP-URL"); import styles from '../styles/common-styles.js'; export default class signup extends Component { constructor(props){ super(props); this.state = { loaded: true, email: '', password: '' }; } signup(){ this.setState({ loaded: false }); app.createUser({ 'email': this.state.email, 'password': this.state.password }, (error, userData) => { if(error){ switch(error.code){ case "EMAIL_TAKEN": alert("The new user account cannot be created because the email is already in use."); break; case "INVALID_EMAIL": alert("The specified email is not a valid email."); break; default: alert("Error creating user:"); } }else{ alert('Your account was created!'); } this.setState({ email: '', password: '', loaded: true }); }); } goToLogin(){ this.props.navigator.push({ component: Login }); } render() { return ( <View style={styles.container}> <Header text="Signup" loaded={this.state.loaded} /> <View style={styles.body}> <TextInput style={styles.textinput} onChangeText={(text) => this.setState({email: text})} value={this.state.email} placeholder={"Email Address"} /> <TextInput style={styles.textinput} onChangeText={(text) => this.setState({password: text})} value={this.state.password} secureTextEntry={true} placeholder={"Password"} /> <Button text="Signup" onpress={this.signup.bind(this)} button_styles={styles.primary_button} button_text_styles={styles.primary_button_text} /> <Button text="Got an Account?" onpress={this.goToLogin.bind(this)} button_styles={styles.transparent_button} button_text_styles={styles.transparent_button_text} /> </View> </View> ); } } AppRegistry.registerComponent('signup', () => signup);
Разбивка кода выше. Сначала импортируйте реагирующую версию и извлекайте все необходимое из класса React
.
import React, { AppRegistry, Component, Text, TextInput, View } from 'react-native';
Импортируйте компоненты кнопки и заголовка:
import Button from '../components/button'; import Header from '../components/header';
Импортируйте страницу входа:
import Login from './login';
Импортируйте библиотеку Firebase и создайте ссылку на приложение Firebase, которое вы создали ранее, указав URL-адрес приложения.
Примечание . Вместо указания полного URL-адреса, такого как http://your-app-name.firebasio.com, следует указать your-app-name.firebaseio.com . Вам также необходимо заменить YOUR-FIREBASE-APP-URL
в каждом файле.
import Firebase from 'firebase'; let app = new Firebase("YOUR-FIREBASE-APP-URL");
Импортируйте общие стили:
import styles from '../styles/common-styles.js';
Создайте новый компонент и экспортируйте его для импорта в другие файлы.
export default class signup extends Component { ... }
В конструкторе установите состояние по умолчанию. loaded
наборы, показывать ли счетчик. Если loaded
true
тогда счетчик скрыт, иначе счетчик виден. email
и password
являются значениями по умолчанию для текстовых полей электронной почты и пароля.
constructor(props){ super(props); this.state = { loaded: true, email: '', password: '' }; }
Метод signup
выполняется, когда пользователь нажимает кнопку регистрации. Первая настройка loaded
в false
чтобы показать счетчик. Затем вызовите метод createUser
в приложении createUser
. Этот метод принимает объект, содержащий электронную почту и пароль пользователя в качестве первого аргумента, и функцию обратного вызова в качестве второго. Если error
не пуста, предупредите пользователя на основе свойства code
error
. В противном случае предположим, что учетная запись была создана. Наконец установите email
и password
в пустую строку, чтобы сбросить значение текстовых полей.
signup(){ this.setState({ loaded: false }); app.createUser({ 'email': this.state.email, 'password': this.state.password }, (error, userData) => { if(error){ switch(error.code){ case "EMAIL_TAKEN": alert("The new user account cannot be created because the email is already in use."); break; case "INVALID_EMAIL": alert("The specified email is not a valid email."); break; default: alert("Error creating user:"); } }else{ alert('Your account was created!'); } this.setState({ email: '', password: '', loaded: true }); }); }
Функция goToLogin
перейти на страницу входа. Это работает с помощью метода push
компонента Navigator. Метод push
принимает объект, содержащий компонент, который вы хотите отобразить.
goToLogin(){ this.props.navigator.push({ component: Login }); }
Метод render
отображает пользовательский интерфейс компонента. Он имеет заголовок, текстовое поле для ввода адреса электронной почты и пароля, кнопку для регистрации и кнопку для перехода на страницу входа.
render() { return ( <View style={styles.container}> <Header text="Signup" loaded={this.state.loaded} /> <View style={styles.body}> <TextInput style={styles.textinput} onChangeText={(text) => this.setState({email: text})} value={this.state.email} placeholder={"Email Address"} /> <TextInput style={styles.textinput} onChangeText={(text) => this.setState({password: text})} value={this.state.password} secureTextEntry={true} placeholder={"Password"} /> <Button text="Signup" onpress={this.signup.bind(this)} button_styles={styles.primary_button} button_text_styles={styles.primary_button_text} /> <Button text="Got an Account?" onpress={this.goToLogin.bind(this)} button_styles={styles.transparent_button} button_text_styles={styles.transparent_button_text} /> </View> </View> ); }
Обратите внимание на значение loaded
в состоянии как значение для loaded
атрибута в заголовке. Это позволяет контролировать показ счетчика из родительского компонента.
<Header text="Signup" loaded={this.state.loaded} />
Для текстовых полей укажите атрибут onChangeText
и передайте функцию стрелки, которая обновит значение этого конкретного поля в состоянии.
onChangeText={(text) => this.setState({password: text})}
Для поля пароля есть другой атрибут с именем secureTextEntry
установленный в true
чтобы указать, что введенные символы должны быть скрыты.
secureTextEntry={true}
Для кнопок обратите внимание на использование bind
для функции signup
вместо непосредственного ее выполнения при нажатии кнопки. Это связано с тем, что методы в es6 не привязываются автоматически к текущему классу.
<Button text="Signup" onpress={this.signup.bind(this)} button_styles={styles.primary_button} button_text_styles={styles.primary_button_text} />
Страница авторизации
Страница входа предназначена для входа в систему пользователей. Создайте pages / login.js и добавьте следующий код:
'use strict'; import React, { AppRegistry, Component, StyleSheet, Text, TextInput, View, AsyncStorage } from 'react-native'; import Button from '../components/button'; import Header from '../components/header'; import Signup from './signup'; import Account from './account'; import Firebase from 'firebase'; let app = new Firebase("YOUR-FIREBASE-APP-URL"); import styles from '../styles/common-styles.js'; export default class login extends Component { constructor(props){ super(props); this.state = { email: '', password: '', loaded: true } } render(){ return ( <View style={styles.container}> <Header text="Login" loaded={this.state.loaded} /> <View style={styles.body}> <TextInput style={styles.textinput} onChangeText={(text) => this.setState({email: text})} value={this.state.email} placeholder={"Email Address"} /> <TextInput style={styles.textinput} onChangeText={(text) => this.setState({password: text})} value={this.state.password} secureTextEntry={true} placeholder={"Password"} /> <Button text="Login" onpress={this.login.bind(this)} button_styles={styles.primary_button} button_text_styles={styles.primary_button_text} /> <Button text="New here?" onpress={this.goToSignup.bind(this)} button_styles={styles.transparent_button} button_text_styles={styles.transparent_button_text} /> </View> </View> ); } login(){ this.setState({ loaded: false }); app.authWithPassword({ "email": this.state.email, "password": this.state.password }, (error, user_data) => { this.setState({ loaded: true }); if(error){ alert('Login Failed. Please try again'); }else{ AsyncStorage.setItem('user_data', JSON.stringify(user_data)); this.props.navigator.push({ component: Account }); } }); } goToSignup(){ this.props.navigator.push({ component: Signup }); } } AppRegistry.registerComponent('login', () => login);
Здесь нет ничего нового, кроме функции login
в login
. Функция login
вызывает метод authWithPassword
из приложения Firebase, передавая объект, содержащий электронную почту и пароль пользователя, и функцию обратного вызова для выполнения после возврата ответа. Если в ответе нет ошибок, используйте AsyncStorage
для сохранения пользовательских данных в локальном хранилище, вызвав метод setItem
в объекте AsyncStorage
. Этот метод принимает имя элемента и его значение.
Примечание . Вы можете хранить только строки, поэтому мы используем метод JSON.stringify
для преобразования объекта user_data
в строку. После этого перейдите на страницу учетной записи или предупредите пользователя о том, что вход не выполнен.
login(){ this.setState({ loaded: false }); app.authWithPassword({ "email": this.state.email, "password": this.state.password }, (error, user_data) => { this.setState({ loaded: true }); if(error){ alert('Login Failed. Please try again'); }else{ AsyncStorage.setItem('user_data', JSON.stringify(user_data)); this.props.navigator.push({ component: Account }); } }); }
Страница аккаунта
На странице учетной записи отображается основная информация о текущем пользователе. Создайте pages / account.js и добавьте следующее:
'use strict'; import React, { AppRegistry, Component, StyleSheet, Text, View, Image, AsyncStorage } from 'react-native'; import Button from '../components/button'; import Header from '../components/header'; import Login from './login'; import styles from '../styles/common-styles.js'; import Firebase from 'firebase'; let app = new Firebase("YOUR-FIREBASE-APP-URL"); export default class account extends Component { constructor(props){ super(props); this.state = { loaded: false, } } componentWillMount(){ AsyncStorage.getItem('user_data').then((user_data_json) => { let user_data = JSON.parse(user_data_json); this.setState({ user: user_data, loaded: true }); }); } render(){ return ( <View style={styles.container}> <Header text="Account" loaded={this.state.loaded} /> <View style={styles.body}> { this.state.user && <View style={styles.body}> <View style={page_styles.email_container}> <Text style={page_styles.email_text}>{this.state.user.password.email}</Text> </View> <Image style={styles.image} source={{uri: this.state.user.password.profileImageURL}} /> <Button text="Logout" onpress={this.logout.bind(this)} button_styles={styles.primary_button} button_text_styles={styles.primary_button_text} /> </View> } </View> </View> ); } logout(){ AsyncStorage.removeItem('user_data').then(() => { app.unauth(); this.props.navigator.push({ component: Login }); }); } } const page_styles = StyleSheet.create({ email_container: { padding: 20 }, email_text: { fontSize: 18 } });
В отличие от других страниц, созданных до сих пор, эта страница имеет метод componentWillMount
. Этот метод выполняется до монтирования компонента, поэтому он является идеальным местом для получения пользовательских данных из локального хранилища. На этот раз он использует метод getItem
из объекта AsyncStorage
, который принимает имя элемента в качестве аргумента. Чтобы получить сохраненное значение, используйте метод then
и передайте функцию. Затем этой функции будет передано значение в качестве аргумента. Преобразуйте значение обратно в объект, используя JSON.parse
затем установите его в текущее состояние. Таким образом, вы можете использовать this.state.user
для извлечения любой информации из объекта пользователя.
componentWillMount(){ AsyncStorage.getItem('user_data').then((user_data_json) => { let user_data = JSON.parse(user_data_json); this.setState({ user: user_data, loaded: true }); }); }
Внутри метода render
находится новый компонент Image
. Это позволяет отображать изображение во многом как элемент img
в HTML, но указав атрибут source
с объектом, содержащим свойство uri
. Это свойство uri
ссылается на URL изображения, которое вы хотите отобразить.
<Image style={styles.image} source={{uri: this.state.user.password.profileImageURL}} />
Стили
Каждый из компонентов включал src / styles / common-styles.js, но он еще не был создан. Файл служит глобальной таблицей стилей для всего приложения. Создайте файл и добавьте следующий код:
'use strict'; import React, { StyleSheet } from 'react-native'; module.exports = StyleSheet.create({ container: { flex: 1, }, body: { flex: 9, alignItems: 'center', backgroundColor: '#F5FCFF', }, textinput: { height: 40, borderColor: 'red', borderWidth: 1 }, transparent_button: { marginTop: 10, padding: 15 }, transparent_button_text: { color: '#0485A9', fontSize: 16 }, primary_button: { margin: 10, padding: 15, backgroundColor: '#529ecc' }, primary_button_text: { color: '#FFF', fontSize: 18 }, image: { width: 100, height: 100 } });
Собираем все вместе
Теперь соберите все вместе, заменив код в index.android.js на приведенный ниже, или index.ios.js, если вы хотите выполнить развертывание на iOS.
'use strict'; import React, { AppRegistry, Component, Text, View, Navigator, AsyncStorage } from 'react-native'; import Signup from './src/pages/signup'; import Account from './src/pages/account'; import Header from './src/components/header'; import Firebase from 'firebase'; let app = new Firebase("YOUR-FIREBASE-APP-URL"); import styles from './src/styles/common-styles.js'; class rnfirebaseauth extends Component { constructor(props){ super(props); this.state = { component: null, loaded: false }; } componentWillMount(){ AsyncStorage.getItem('user_data').then((user_data_json) => { let user_data = JSON.parse(user_data_json); let component = {component: Signup}; if(user_data != null){ app.authWithCustomToken(user_data.token, (error, authData) => { if(error){ this.setState(component); }else{ this.setState({component: Account}); } }); }else{ this.setState(component); } }); } render(){ if(this.state.component){ return ( <Navigator initialRoute={{component: this.state.component}} configureScene={() => { return Navigator.SceneConfigs.FloatFromRight; }} renderScene={(route, navigator) => { if(route.component){ return React.createElement(route.component, { navigator }); } }} /> ); }else{ return ( <View style={styles.container}> <Header text="React Native Firebase Auth" loaded={this.state.loaded} /> <View style={styles.body}></View> </View> ); } } } AppRegistry.registerComponent('rnfirebaseauth', () => rnfirebaseauth);
Метод componentWillMount
проверяет, хранятся ли user_data
в локальном хранилище. Как и на предыдущей странице учетной записи, используйте AsyncStorage.getItem
чтобы получить данные из локального хранилища, а затем проанализировать их. Если он возвращает null
, то предположим, что в локальном хранилище ничего нет, и обновите состояние, чтобы установить страницу регистрации в качестве текущей страницы. В противном случае, попытайтесь аутентифицировать пользователя с токеном, который он пытался выполнить, используя последний адрес электронной почты и пароль, вызвав app.authWithCustomToken
и передав ему токен. Если это удастся, установите текущую страницу на страницу учетной записи или на страницу регистрации.
componentWillMount(){ AsyncStorage.getItem('user_data').then((user_data_json) => { let user_data = JSON.parse(user_data_json); let component = {component: Signup}; if(user_data != null){ app.authWithCustomToken(user_data.token, (error, authData) => { if(error){ this.setState(component); }else{ this.setState({component: Account}); } }); }else{ this.setState(component); } }); }
Внутри метода render
проверьте, установлен ли компонент в состоянии. Как вы видели ранее из метода constructor
, это значение равно null
поэтому оператор else
будет выполнен по умолчанию. Внутри оператора else
находится пользовательский интерфейс по умолчанию, который отображается при открытии приложения. После обновления состояния метод render
вызывается снова, на этот раз выполняется код внутри условия if
.
if(this.state.component){ return ( <Navigator initialRoute={{component: this.state.component}} configureScene={() => { return Navigator.SceneConfigs.FloatFromRight; }} renderScene={(route, navigator) => { if(route.component){ return React.createElement(route.component, { navigator }); } }} /> ); }else{ return ( <View style={styles.container}> <Header text="React Native Firebase Auth" loaded={this.state.loaded} /> <View style={styles.body}></View> </View> ); }
Внутри условия if
компонент Navigator
обрабатывает переходы между страницами. Это принимает initialRoute
и renderScene
и необязательный атрибут configureScene
для настройки анимации при переходе между страницами. initialRoute
позволяет вам указать объект, содержащий информацию о компоненте по умолчанию для визуализации с помощью навигатора. Метод renderScene
принимает функцию, которая будет визуализировать компонент с route
и navigator
переданными в качестве аргумента этой функции. route
— это объект, переданный в initialRoute
.
С помощью route.component
вы получаете фактическую ссылку на компонент и отображаете ее, используя React.createElement
. Второй аргумент — это объект, содержащий props
вы хотите передать визуализированному компоненту. В этом случае передается объект navigator
, который содержит все методы, необходимые для навигации между различными страницами.
Если вы посмотрите на код для каждой из страниц (логин, регистрация, учетная запись), вы увидите, что объект navigator
используется как this.props.navigator
поскольку он был передан как props
.
<Navigator initialRoute={{component: this.state.component}} configureScene={() => { return Navigator.SceneConfigs.FloatFromRight; }} renderScene={(route, navigator) => { if(route.component){ return React.createElement(route.component, { navigator }); } }} />
Что дальше?
В этом руководстве вы создали приложение, которое аутентифицирует пользователей, использующих Firebase, с помощью комбинации адреса электронной почты и пароля. Firebase предлагает гораздо больше возможностей, когда дело доходит до аутентификации. Вы, возможно, заметили ранее, когда создавали приложение, которое Firebase позволяет вам использовать логины Facebook, Twitter, Github, Google, Anonymous и Custom. Если вы ищете другие способы аутентификации своих пользователей, я рекомендую вам проверить эти параметры.
Вы также узнали, как использовать AsyncStorage
для локального сохранения пользовательских данных. Это позволяет приложению сохранять состояние входа в систему при последующих запусках приложения.
Firebase предоставляет вам функциональность, необходимую для мобильных приложений. Надеюсь, вы нашли этот урок полезным и приветствуем ваши комментарии и вопросы.