16-3 RN之创建登录页面并跳转

138 阅读2分钟

在本节中,我们完成了账号模块的功能,以下是登录页面的实现:

一、创建登录页面组件

  1. 创建 Login 组件

    pages 文件夹下创建 Login 文件夹,并添加 index.tsx 文件:

    import React from 'react';
    import { View, Text, ScrollView, StyleSheet } from 'react-native';
    import { Formik } from 'formik';
    import * as Yup from 'yup';
    import CustomInput from './CustomInput';
    import { connect, ConnectedProps } from 'react-redux';
    import { RootState } from '@/models/index';
    import { TouchableOpacity } from 'react-native';
    
    const validationSchema = Yup.object().shape({
      username: Yup.string().required('账号不能为空'),
      password: Yup.string().required('密码不能为空'),
    });
    
    interface IProps extends ConnectedProps<typeof connector> {}
    
    class Login extends React.PureComponent<IProps> {
      handleSubmit = (values: { username: string; password: string }) => {
        const { dispatch } = this.props;
        dispatch({
          type: 'user/login',
          payload: values,
        });
      };
    
      render() {
        return (
          <ScrollView keyboardShouldPersistTaps="handled" style={styles.container}>
            <Text style={styles.logo}>听书</Text>
            <Formik
              initialValues={{ username: '', password: '' }}
              validationSchema={validationSchema}
              onSubmit={this.handleSubmit}
            >
              {({
                handleChange,
                handleBlur,
                handleSubmit,
                values,
                errors,
                touched,
              }) => (
                <>
                  <CustomInput
                    placeholder="请输入账号"
                    value={values.username}
                    onChangeText={handleChange('username')}
                    onBlur={handleBlur('username')}
                    error={errors.username}
                    touched={touched.username}
                  />
                  <CustomInput
                    placeholder="请输入密码"
                    value={values.password}
                    onChangeText={handleChange('password')}
                    onBlur={handleBlur('password')}
                    error={errors.password}
                    touched={touched.password}
                    secureTextEntry
                  />
                  <TouchableOpacity
                    style={styles.loginButton}
                    onPress={handleSubmit}
                  >
                    <Text style={styles.loginButtonText}>登录</Text>
                  </TouchableOpacity>
                </>
              )}
            </Formik>
          </ScrollView>
        );
      }
    }
    
    const styles = StyleSheet.create({
      container: {
        flex: 1,
        alignItems: 'center',
        padding: 20,
        paddingBottom: 200,
      },
      logo: {
        color: '#ff4000',
        fontWeight: 'bold',
        fontSize: 50,
        textAlign: 'center',
        marginTop: 40,
      },
      loginButton: {
        backgroundColor: '#007bff',
        padding: 10,
        borderRadius: 5,
        marginTop: 20,
      },
      loginButtonText: {
        color: '#fff',
      },
    });
    
    const mapStateToProps = (state: RootState) => ({
      user: state.user,
    });
    
    const connector = connect(mapStateToProps);
    
    export default connector(Login);
    
  2. 创建自定义输入控件

    pages/Login 文件夹中添加 CustomInput.tsx 文件:

    import React from 'react';
    import { View, TextInput, Text, StyleSheet } from 'react-native';
    
    interface IProps {
      placeholder: string;
      value: string;
      onChangeText: (text: string) => void;
      onBlur: () => void;
      error?: string;
      touched?: boolean;
      secureTextEntry?: boolean;
    }
    
    const CustomInput = (props: IProps) => {
      return (
        <View style={styles.container}>
          <TextInput
            style={styles.input}
            placeholder={props.placeholder}
            value={props.value}
            onChangeText={props.onChangeText}
            onBlur={props.onBlur}
            secureTextEntry={props.secureTextEntry}
          />
          {props.touched && props.error && (
            <Text style={styles.errorText}>{props.error}</Text>
          )}
        </View>
      );
    };
    
    const styles = StyleSheet.create({
      container: {
        marginBottom: 20,
      },
      input: {
        borderWidth: 1,
        borderColor: '#ccc',
        padding: 10,
        borderRadius: 5,
        width: '100%',
      },
      errorText: {
        color: 'red',
        fontSize: 12,
        paddingHorizontal: 5,
      },
    });
    
    export default CustomInput;
    
  3. 更新导航器

    navigator 文件夹中更新 ModalStackScreen

    <ModalStack.Screen
      name="Login"
      component={Login}
      options={{
        headerTitle: '登录',
      }}
    />;
    

二、更新账号页面组件

  1. 创建 Account 组件

    pages 文件夹下创建 Account 文件夹,并添加 index.tsx 文件:

    import React from 'react';
    import { View, Text, Image, StyleSheet, TouchableOpacity } from 'react-native';
    import { connect, ConnectedProps } from 'react-redux';
    import { RootState } from '@/models/index';
    import defaultAvatar from '@/assets/images/default_avatar.png';
    
    interface IProps extends ConnectedProps<typeof connector> {}
    
    class Account extends React.PureComponent<IProps> {
      goLogin = () => {
        const { navigation } = this.props;
        navigation.navigate('Login');
      };
    
      render() {
        const { user, isLoading } = this.props;
        return (
          <View style={styles.container}>
            <View style={styles.loginView}>
              <Image source={defaultAvatar} style={styles.image} />
              <View style={styles.right}>
                <TouchableOpacity onPress={this.goLogin} style={styles.loginButton}>
                  <Text style={styles.loginButtonText}>立即登录</Text>
                </TouchableOpacity>
                <Text style={styles.tip}>登录后自动同步所有记录哦~</Text>
              </View>
            </View>
          </View>
        );
      }
    }
    
    const mapStateToProps = (state: RootState) => ({
      user: state.user.user,
      isLoading: state.user.isLoading,
    });
    
    const connector = connect(mapStateToProps);
    
    const styles = StyleSheet.create({
      container: {
        flex: 1,
        alignItems: 'center',
        padding: 15,
      },
      loginView: {
        flexDirection: 'row',
        alignItems: 'center',
        margin: 15,
      },
      right: {
        marginLeft: 15,
        justifyContent: 'center',
      },
      image: {
        width: 70,
        height: 70,
        borderRadius: 35,
      },
      loginButton: {
        justifyContent: 'center',
        alignItems: 'center',
        height: 26,
        width: 76,
        borderRadius: 13,
        borderColor: '#f86442',
        borderWidth: 1,
        marginBottom: 7,
      },
      loginButtonText: {
        color: '#f86442',
        fontWeight: '900',
      },
      tip: {
        color: '#666',
        fontSize: 12,
      },
    });
    
    export default connector(Account);
    

三、总结

在本节中,我们创建了登录页面组件,并实现了账号页面的基本 UI 和跳转功能。通过使用 Formikyup,我们实现了表单数据的管理和校验。下一节,我们将实现登录功能的逻辑和状态管理。