16-6 RN之封装表单输入框

32 阅读3分钟

课程目标

本节课程将学习如何使用 Formik 提供的 Field 组件,将表单输入框和错误提示封装成一个通用的组件,以减少重复代码,并提升代码的复用性和可维护性。


1. 为什么要封装?

在上一节中,我们为每个输入框都绑定了 onChangeTextonBlur 事件,同时还手动显示错误提示信息。这些逻辑在多个输入框中重复出现。

通过封装一个通用的组件,我们可以:

  • 减少重复代码
  • 提升代码的复用性
  • 提高代码的可读性和可维护性

2. 使用 Formik 的 Field 组件

Formik 提供了 Field 组件,它是一个高阶函数,封装了以下功能:

  • 自动绑定 onChangeonBlur 和 value 等事件
  • 提供 name 属性,用于指定字段名称
  • 渲染自定义的组件

Field 的基本用法

以下是 Field 的基本用法:

<Field
  name="account" // 表单字段的名称
  component={Input} // 渲染的自定义组件
/>
<Field
  name="password"
  placeholder="请输入密码"
  component={Input}
  secureTextEntry
/>

3. 创建 Input 组件

3.1 创建组件文件

components/ 目录下创建一个名为 Input.tsx 的文件。

3.2 Input 组件代码

以下是 Input 组件的完整代码:

import React from 'react';
import { View, Text, StyleSheet, TextInput, TextInputProps } from 'react-native';
import { FieldInputProps, FormikProps } from 'formik';

// 定义组件的 Props 类型
interface IProps extends TextInputProps {
  field: FieldInputProps<any>; // Formik 提供的字段属性
  form: FormikProps<any>; // Formik 提供的表单属性
}

class Input extends React.PureComponent<IProps> {
  // 自定义 onChangeText 方法
  onChangeText = (value: string) => {
    const { form, field, onChangeText } = this.props;
    console.log('---field', field.name);

    // 调用 Formik 提供的 handleChange 方法
    form.handleChange(field.name)(value);

    // 调用父组件传递的 onChangeText 方法(如果存在)
    if (onChangeText) {
      onChangeText(value);
    }
  };

  render() {
    const { form, field, ...rest } = this.props; // 解构 Props
    return (
      <View style={styles.container}>
        {/* 输入框 */}
        <TextInput
          style={styles.input}
          {...rest}
          onChangeText={form.handleChange(field.name)} // 绑定 Formik  handleChange
          onBlur={form.handleBlur(field.name)} // 绑定 Formik  handleBlur
        />
        {/* 错误提示 */}
        <View>
          <Text style={styles.error}>{form.errors[field.name]}</Text>
        </View>
      </View>
    );
  }
}

// 样式
const styles = StyleSheet.create({
  container: {
    marginVertical: 10,
  },
  input: {
    height: 40,
    paddingHorizontal: 10,
    borderColor: '#ccc',
    borderBottomWidth: StyleSheet.hairlineWidth,
  },
  error: {
    position: 'absolute',
    color: 'red',
    marginTop: 5,
    marginLeft: 10,
    fontSize: 12,
  },
});

export default Input;

4. 在表单中使用封装的 Input 组件

4.1 替换 TextInput

将原来的 TextInput 替换为 Field 组件,并使用自定义的 Input 组件:

<Field
  name="account"
  placeholder="请输入账号"
  component={Input}
/>
<Field
  name="password"
  placeholder="请输入密码"
  component={Input}
  secureTextEntry
/>

4.2 添加 placeholder

通过 Fieldplaceholder 属性,可以为输入框添加占位符。


5. 测试功能

  1. 启动模拟器,打开登录页面。

  2. 验证以下功能:

    • 输入框绑定了正确的字段。
    • 校验失败时,显示对应的错误提示。
  3. 确认封装后表单功能是否正常。


6. 登录成功后的返回操作

6.1 添加返回函数

utils 目录下创建一个返回函数:

function goBack() {
  if (navigationRef.current) {
    navigationRef.current.goBack();
  }
}

export { goBack };

6.2 在登录逻辑中调用返回函数

login.tsx 文件中,调用 goBack 方法:

onSubmit = (values: Values) => {
  const { dispatch } = this.props;

  dispatch({
    type: 'login/login',
    payload: values,
    callback: () => {
      goBack(); // 登录成功后返回上一页
    },
  });
};

7. 小结

7.1 本节内容总结

  1. 封装了通用的 Input 组件,减少重复代码。
  2. 使用 Formik 的 Field 组件,实现了事件绑定和错误提示的自动化处理。
  3. 实现了登录成功后的返回操作。

7.2 思想总结

本节重点在于封装的思想

  • 将重复的逻辑抽象为通用组件。
  • 提升代码的复用性和可维护性。

虽然目前的表单较为简单,但在复杂表单中,这种封装方式可以显著减少代码量,并提升开发效率。


8. 下一节预告

在下一节中,我们将学习如何将登录信息保存到本地存储中,以便用户下次打开应用时能够记住登录状态。