ReactNative 进阶之Form表单组件封装

React Native封装Form表单组件

为什么我们在移动端开发中急需要一个完善的Form表单组件尼?做过移动端开发的小伙伴们应该都清楚,一个完整的App项目中避免不了要有登录注册功能,用户信息,密码等一系列需要新用户填写信息的页面,这些页面的UI如果都是按部就班的去绘制,其实是做了很多无用功,因为大部分UI都长的差不多,就是有些文案类的略微不同,既然这样,我们将这些长的差不多的UI抽离成组件,在需要用到的地方,直接调用封装好的组件,这样极大的降低了编写代码量,大大的提高开发者的效率

Form表单支持的可配置项

  • TextInput
  • TextArea
  • Button
  • ButtonGroup
  • CheckBox
  • Radio
  • Switch
  • DatePicker
  • CustomView
  • Selector
  • CustomAction

后续待添加的配置项

  • 地区选择
  • CustomSelector
  • 用户图像
  • 图片上传
  • 短信验证码
  • 登录注册
  • 待补充

效果图

image

主要功能

后期完成登录注册、身份认证等表单信息页面的开发,我们只需要一个简单的配置文件即可搞定,不必每次相同的表单项都重新绘制一遍UI。对于哪些不常见的表单项,我们可以完全使用自定义UI来搞定,这样一来,我们在平时开发中的80%的表单需求都能轻松的搞定了

核心代码

renderFields(fields) {

    let fieldCount = fields.length ? fields.length : 0

    return fields.map((field, index) => {

      let isLastField = true
      if (fieldCount === index + 1 || fields[index + 1].title !== undefined) {
        isLastField = false
      }

      if (field.title !== undefined && field.show !== false) {
        return this.renderGroupForm(field)
      } else {
        let {show, type, label, subLabel, subTitle, ...other} = field
        let otherProps = {...other}
        Object.keys(otherProps).map((key, index) => {
          if (key.indexOf('on') === 0 || key === 'callback' || key === 'dataFunc' || key === 'view') {
            otherProps[key] = this.props[otherProps[key]]
          }
        })

        let isEditable = !this.props.readOnly && !this._isReadOnly(field.readOnly)

        if (show !== false) {
          if (this.fieldsType[type]) {
            if (type === 'TextInput') {
              return (
                <View
                  key={index}
                  style={[styles.row, !isLastField ? {borderBottomWidth: 1, borderBottomColor: commonStyle.lineColor} : null]}
                >
                  <View style={[styles.content, isLastField ? styles.lastLine : null]}>
                    {
                      label ?
                        <View style={styles.leftPanel}>
                          <View style={styles.leftTitle}>
                            <Text style={{fontSize: 15, color: commonStyle.textBlockColor}}>{label}</Text>
                            <Text style={{fontSize: 20, color: commonStyle.red}}>{subLabel}</Text>
                          </View>
                          {
                            subTitle ?
                              <View style={styles.subTitle}>
                                <Text style={{fontSize: 12, color: commonStyle.textGrayColor}}>{subTitle}</Text>
                              </View> : null
                          }
                        </View> : null
                    }
                    <View style={styles.inputContainer}>
                      <TextInput
                        style={styles.textInput}
                        {...otherProps}
                        placeholderTextColor={commonStyle.placeholderTextColor}
                        underlineColorAndroid={'transparent'}
                        editable={isEditable}
                        value={this.state[field.key] !== undefined && this.state[field.key] !== null && this.state[field.key].toString()}
                        onChangeText={value => {
                          this.state[field.key] = value
                          this.setState(this.state)
                          let _obj = {}
                          _obj[field.key] = value
                          this.props.onChange && this.props.onChange(_obj)
                        }}
                      />
                    </View>
                  </View>
                </View>
              )
            } else if (type === 'Button') {
              return (
                <View
                  key={index}
                  style={[styles.row, !isLastField ? {borderBottomWidth: 1, borderBottomColor: commonStyle.lineColor} : null]}>
                  <View
                    style={[styles.content, isLastField ? styles.lastLine : null, {justifyContent: commonStyle.center}]}>
                    <Button
                      {...field}
                      {...otherProps}>
                      {field.text}
                    </Button>
                  </View>
                </View>
              )
            } else if (type === 'ButtonGroup') {
              return (
                <View
                  key={index}
                  style={[styles.row, !isLastField ? {borderBottomWidth: 1, borderBottomColor: commonStyle.lineColor} : null]}>
                  <View style={[styles.content, isLastField ? styles.lastLine : null]}>
                    {
                      label ?
                        <View style={styles.leftPanel}>
                          <View style={styles.leftTitle}>
                            <Text style={{fontSize: 15, color: commonStyle.textBlockColor}}>{label}</Text>
                            <Text style={{fontSize: 20, color: commonStyle.red}}>{subLabel}</Text>
                          </View>
                          {
                            subTitle ?
                              <View style={styles.subTitle}>
                                <Text style={{fontSize: 12, color: commonStyle.textGrayColor}}>{subTitle}</Text>
                              </View> : null
                          }
                        </View> : null
                    }
                    <View style={{flex: 1, height: 49, flexDirection: commonStyle.row, alignItems: commonStyle.center, justifyContent: 'flex-end', marginRight: 10}}>
                      {
                        field.items.map((item, index) => {
                          let _cloneBtn = deepClone(item)
                          Object.keys(_cloneBtn).map((key, index) => {
                            if (key.indexOf('on') === 0) {
                              _cloneBtn[key] = this.props[_cloneBtn[key]]
                            }
                          })
                          if (typeof _cloneBtn.selected === 'boolean') {
                            return (
                              <Button
                                key={index}
                                style={[_cloneBtn.style, _cloneBtn.selected ? _cloneBtn.selectedStyle : null]}
                                textStyle={[_cloneBtn.textStyle, _cloneBtn.selected ? _cloneBtn.textSelectedStyle : null]}
                                {..._cloneBtn}
                              >
                                {_cloneBtn.text}
                              </Button>
                            )
                          } else {
                            return (
                              <Button
                                key={index}
                                style={[_cloneBtn.style, _cloneBtn.selected && _cloneBtn.selected() ? _cloneBtn.selectedStyle : null]}
                                textStyle={[_cloneBtn.textStyle, _cloneBtn.selected && _cloneBtn.selected() ? _cloneBtn.textSelectedStyle : null]}
                              >
                                {_cloneBtn.text}
                              </Button>
                            )
                          }
                        })
                      }
                    </View>
                  </View>
                </View>
              )
            } else if (type === 'Switch') {
              return (
                <View
                  key={index}
                  style={[styles.row, !isLastField ? {borderBottomWidth: 1, borderBottomColor: commonStyle.lineColor} : null]}
                >
                  <View style={[styles.content, isLastField ? styles.lastLine : null]}>
                    {
                      label ?
                        <View style={styles.leftPanel}>
                          <View style={styles.leftTitle}>
                            <Text style={{fontSize: 15, color: commonStyle.textBlockColor}}>{label}</Text>
                            <Text style={{fontSize: 20, color: commonStyle.red}}>{subLabel}</Text>
                          </View>
                          {
                            subTitle ?
                              <View style={styles.subTitle}>
                                <Text style={{fontSize: 12, color: commonStyle.textGrayColor}}>{subTitle}</Text>
                              </View> : null
                          }
                        </View> : null
                    }
                    <View style={styles.switch}>
                      <Switch
                        disabled={!isEditable}
                        value={this.state[field.key] !== undefined && this.state[field.key] !== null && this.state[field.key]}
                        onValueChange={value => {
                          this.state[field.key] = value
                          this.setState(this.state)
                          let _obj = {}
                          _obj[field.key] = value
                          this.props.onChange && this.props.onChange(_obj)
                        }}
                      />
                    </View>
                  </View>
                </View>
              )
            } else if (type === 'View') {
              return (
                <View key={index}
                      style={[styles.row, !isLastField ? {borderBottomWidth: 1, borderBottomColor: commonStyle.lineColor} : null]}>
                  <View
                    key={index}
                    style={[styles.content, isLastField ? styles.lastLine : null]}>
                    {
                      label ?
                        <View style={styles.leftPanel}>
                          <View style={styles.leftTitle}>
                            <Text style={{fontSize: 15, color: commonStyle.textBlockColor}}>{label}</Text>
                            <Text style={{fontSize: 20, color: commonStyle.red}}>{subLabel}</Text>
                          </View>
                          {
                            subTitle ?
                              <View style={styles.subTitle}>
                                <Text style={{fontSize: 12, color: commonStyle.textGrayColor}}>{subTitle}</Text>
                              </View> : null
                          }
                        </View> : null
                    }
                    <View
                      ref={'customView'}
                      style={styles.view}
                      {...field}
                      {...otherProps}
                    >
                      {otherProps.view}
                    </View>
                  </View>
                </View>
              )
            } else if (type === 'TextArea') {
              let Comp = this.fieldsType[type]
              return (
                <View key={index}
                      style={[styles.row, !isLastField ? {borderBottomWidth: 1, borderBottomColor: commonStyle.lineColor} : null]}>
                  <View
                    key={index}
                    style={[styles.content, isLastField ? styles.lastLine : null]}>
                    {
                      label ?
                        <View style={styles.leftPanel}>
                          <View style={styles.leftTitle}>
                            <Text style={{fontSize: 15, color: commonStyle.textBlockColor}}>{label}</Text>
                            <Text style={{fontSize: 20, color: commonStyle.red}}>{subLabel}</Text>
                          </View>
                          {
                            subTitle ?
                              <View style={styles.subTitle}>
                                <Text style={{fontSize: 12, color: commonStyle.textGrayColor}}>{subTitle}</Text>
                              </View> : null
                          }
                        </View> : null
                    }
                    <Comp
                      ref={(ref) => this._component[field.key] = ref}
                      name={field.key}
                      value={this.state}
                      editable={isEditable}
                      {...field}
                      {...otherProps}
                      onChange={this.props.onChange && this.props.onChange()}
                    />
                  </View>
                </View>
              )
            } else {
              let Comp = this.fieldsType[type]
              return (
                <View key={index}
                      style={[styles.row, !isLastField ? {borderBottomWidth: 1, borderBottomColor: commonStyle.lineColor} : null]}>
                  <View
                    key={index}
                    style={[styles.content, isLastField ? styles.lastLine : null]}>
                    {
                      label ?
                        <View style={styles.leftPanel}>
                          <View style={styles.leftTitle}>
                            <Text style={{fontSize: 15, color: commonStyle.textBlockColor}}>{label}</Text>
                            <Text style={{fontSize: 20, color: commonStyle.red}}>{subLabel}</Text>
                          </View>
                          {
                            subTitle ?
                              <View style={styles.subTitle}>
                                <Text style={{fontSize: 12, color: commonStyle.textGrayColor}}>{subTitle}</Text>
                              </View> : null
                          }
                        </View> : null
                    }
                    <View style={{flex: 1}}>
                      <Comp
                        ref={(ref) => this._component[field.key] = ref}
                        name={field.key}
                        value={this.state}
                        editable={isEditable}
                        {...field}
                        {...otherProps}
                        descValue={field.name}
                        onChange={this.props.onChange && this.props.onChange()}
                      />
                    </View>
                  </View>
                </View>
              )
            }
          } else {
            return (
              <View key={index}
                    style={[styles.row, !isLastField ? {borderBottomWidth: 1, borderBottomColor: commonStyle.lineColor} : null]}>
                <View
                  key={index}
                  style={[styles.content, isLastField ? styles.lastLine : null]}>
                  {
                    label ?
                      <View style={styles.leftPanel}>
                        <View style={styles.leftTitle}>
                          <Text style={{fontSize: 15, color: commonStyle.textBlockColor}}>{label}</Text>
                          <Text style={{fontSize: 20, color: commonStyle.red}}>{subLabel}</Text>
                        </View>
                        {
                          subTitle ?
                            <View style={styles.subTitle}>
                              <Text style={{fontSize: 12, color: commonStyle.textGrayColor}}>{subTitle}</Text>
                            </View> : null
                        }
                      </View> : null
                  }
                  <View style={{ flex: 1, height: 49 }}>
                    <CustomAction
                      type={type}
                      ref={(ref) => this._component[field.key] = ref}
                      name={field.key} value={this.state}
                      {...field}
                      {...otherProps}
                      editable={isEditable}
                      onChange={this.props.onChange}
                      descValue={field.name}
                      />
                  </View>
                </View>
              </View>
            )
          }
        }
      }
    })
  }

详细的使用方式请参照项目源码OneM

https://github.com/guangqiang-liu/OneM

更多文章

  • 作者React Native开源项目OneM【500+ star】地址(按照企业开发标准搭建框架完成开发的):https://github.com/guangqiang-liu/OneM:欢迎小伙伴们 star
  • 作者简书主页:包含60多篇RN开发相关的技术文章http://www.jianshu.com/u/023338566ca5 欢迎小伙伴们:多多关注多多点赞
  • 作者React Native QQ技术交流群:620792950 欢迎小伙伴进群交流学习
  • 友情提示:在开发中有遇到RN相关的技术问题,欢迎小伙伴加入交流群(620792950),在群里提问、互相交流学习。交流群也定期更新最新的RN学习资料给大家,谢谢大家支持!

小伙伴们扫下方二维码加入RN技术交流QQ群

QQ群二维码,500+ RN工程师在等你加入哦
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,752评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,100评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,244评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,099评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,210评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,307评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,346评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,133评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,546评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,849评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,019评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,702评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,331评论 3 319
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,030评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,260评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,871评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,898评论 2 351

推荐阅读更多精彩内容