JNUfeatherwit / blog

6 stars 0 forks source link

React Native之登录验证 #4

Open JNUfeatherwit opened 6 years ago

JNUfeatherwit commented 6 years ago

本文介绍我们项目用到的一种登录验证的方案。 完成一套登录流程需要解决以下的问题:

1、登录的用户信息和行为用什么形式保存在内存中,如何将用户信息传播到所有需要使用到它的组件

type Props = { appStore: AppStore, navigation: NavigationScreenProp<*> }

@inject('appStore') @observer class App extends Component { form = new LoginForm()

navigateHome = async () => { // 获取用户配置信息 await this.props.appStore.getUserConfiguration() this.props.navigation.navigate('Home') }

loginSuccess = async () => { // 登录成功获取用户登录信息 await this.props.appStore.getCurrentLoginInformation() await this.navigateHome() }

login = async () => { if (this.form.validateFields()) { try { await this.props.appStore.login(this.form) await this.loginSuccess() } catch (e) { console.warn(e) Toast.fail('用户名或密码错误!', 2, undefined, false) } } else { Toast.fail('请输入正确的用户名和密码!', 2, undefined, false) } }

render() { return (

账号 密码
)

} }

const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center' } })

class LoginForm { @observable @validator([ { required: true, message: '用户名不能为空' } ]) userName = ''

@observable @validator([ { required: true, message: '密码不能为空' } ]) password = '' }

export default App

先自定义一个class LoginForm作为表单的数据对象,用@validator修饰每个属性赋予其验证的功能,再将这个form传递给FormProvider。

//FormProvider import React, { Component } from 'react' import { View } from 'react-native' import { observer } from 'mobx-react' import { observable, action } from 'mobx' import PropTypes from 'prop-types'

@observer class FormProvider extends Component { static propTypes = { form: PropTypes.object.isRequired }

@observable validateStatus = false

constructor(props, context) { super(props, context)

/**
 * 校验全部组件
 */
this.props.form.validateFields = action(() => {
  this.validateStatus = true
  return this.props.form.isValid
})
/**
 * 获取全部组件的值
 */
this.props.form.getFieldsValue = () => {
  return this.props.form
}

}

render() { const { form, children } = this.props return (

{React.Children.map(children, children => { return React.cloneElement(children, { form, validateStatus: this.validateStatus }) })}
)

} }

export default FormProvider

Provider是一个总的表单组件,它赋予传递过来的form两个方法:验证和获取表单,加载FormItem表单项组件

//FormItem import React, { Component } from 'react' import PropTypes from 'prop-types' import { observer } from 'mobx-react' import { observable, action } from 'mobx' import camelCase from 'camelcase' import { Toast, InputItem } from 'antd-mobile-rn'

@observer class FormItem extends Component { static propTypes = { form: PropTypes.object, name: PropTypes.string.isRequired, placeholder: PropTypes.string, type: PropTypes.string, editable: PropTypes.bool, disabled: PropTypes.bool, clear: PropTypes.bool, extra: PropTypes.string, onExtraClick: () => {}, labelNumber: PropTypes.number, labelPosition: PropTypes.string, textAlign: PropTypes.string, last: PropTypes.bool }

validateKey = camelCase(validateError ${this.props.name})

@observable focus = false

showError = () => { Toast.fail(this.props.form[this.validateKey], 2, undefined, false) }

@action onChangeText = text => { const { name } = this.props this.props.form[name] = text }

@action onFocus = () => { if (!this.focused) { this.focused = true } }

render() { const { name, form, validateStatus, children, placeholder, type, editable, clear, disabled, extra, onExtraClick, labelNumber, labelPosition, textAlign, last,

  ...otherProps
} = this.props
const value = form[name]
// 是否校验通过
const isError = (!!form[this.validateKey] && this.focused) || (!!form[this.validateKey] && validateStatus)

return (
  <InputItem
    {...otherProps}
    value={value}
    error={isError}
    onFocus={this.onFocus}
    onChange={this.onChangeText}
    onErrorClick={this.showError}
    placeholder={placeholder}
    type={type}
    editable={editable}
    clear={clear}
    disabled={disabled}
    extra={extra}
    onExtraClick={onExtraClick}
    labelNumber={labelNumber}
    labelPosition={labelPosition}
    textAlign={textAlign}
    last={last}
  >
    {children}
  </InputItem>
)

} }

export default FormItem

form[this.validateKey]是表单项对应的报错信息,isError规定了只有在触发了onfocus或validateFields()且不报错的时候才会在输入框右边弹出小感叹号, showError规定了点击感叹号时弹出一个Toast。
### 3、如何登录验证
不同于输入验证,登录验证不仅要先初步判断输入项是否为空,而且要进行服务端验证,这就需要用到异步的方法,代码如下:

login = async () => { if (this.form.validateFields()) { try { await this.props.appStore.login(this.form) await this.loginSuccess() } catch (e) { console.warn(e) Toast.fail('用户名或密码错误!', 2, undefined, false) } } else { Toast.fail('请输入正确的用户名和密码!', 2, undefined, false) } }

 先是调用this.form.validateFields()进行初步判断,然后调用异步方法login进行登录,这是登录的具体代码

@loading @action async login(user: { userName: string, password: string }) { const { accessToken, userId } = await fech(url,{userName,password}) await setStorageValue('Authorization', accessToken) //存储在本地存储 return userId }

先是获取用户数据,再把登录权限存在本地,之后第二次登录可以直接跳过登录。
登录成功后调用loginSuccess(),代码如下:

loginSuccess = async () => { // 登录成功获取用户登录信息 await this.props.appStore.getCurrentLoginInformation() //获取当前用户的信息,判断是否在登录状态 await this.navigateHome() //跳转Home 页面 }