翻译|Simple React Native forms with redux-form, immutable.js and styled-components - Esben Petersen


title: 翻译|Simple React Native forms with redux-form, immutable.js and styled-components - Esben Petersen
date: 2017-04-03 16:07:15
categories: 翻译
tags: Redux


在React中处理表单还不怎么好弄,看看这篇文章讲的挺好的看看,试着翻译一下
原文请参看原文

start

如果你想看看怎么在react-native 想使用redux-form和immutable.js.你看可以看看这篇文章的代码

Introduction

Traede,我使用redux整合redux-form来创建表单.redux-form是一个非常好的库,但是没有什么好的示例代码介绍怎么和React-native配合使用.所以当我开始在React-Natvie中使用redux-form的时候,发现还是有一点难度的.大多数的情况都因为我发现在React-Native中使用Immutable.js的时候会中断redux-form运行的bug.Redux-form主版本号已经到了6.4.2了.所以我考虑可以根据我的学习过程来写一点怎么在应用中同时使用redux-form和Immutable.js的文档.

Agenda

大体的步骤是:

  1. 看看redux-form在native和web之间的差异点
  2. 看看怎么使redux-form和Immutable.js一起使用
  3. 使用react-native-clean-form的完整实例

鸡冻不?好了,开始了

在React-native中使用redux-form

如果你不太熟悉redux-form,我建议你看看redux-form简单文档
.注意:这个指南假定你使用的redux-form的版本大于6.4.2.如果不是这个版本,并且还在使用Immutable.js,那么要看看这个issue.创建一个表单基础的三个步骤是:

  1. 在你的redux store中加入redux-form reducer
  2. 使用reduxForm包装器把你的表单连接到store
  3. 使用Field包装器把特定的表单字段连接到Store

0. 创建React Native 项目

假设你已经有了可以运行的React Native项目.如果没有,使用react-native init MyReduxFormProjct终端命令行命令也很容易创建项目.

1.把redux-form reducer添加到你的redux store

做这一步,请参考redux-form文档

2.使用redux-form包装器把你的form连接到store

我们使用一个简单的表单来开始工作.

import React from 'react'
import {
  StyleSheet,
  Text,
  TextInput,
  TouchableOpacity,
  View
} from 'react-native'

const Form = props => {
  return (
    <View style={styles.container}>
      <Text>Email:</Text>
      <TextInput style={styles.input} />
      <TouchableOpacity>
        <Text style={styles.button}>Submit</Text>
      </TouchableOpacity>
    </View>
  )
}

export default Form

const styles = StyleSheet.create({
  button: {
    backgroundColor: 'blue',
    color: 'white',
    height: 30,
    lineHeight: 30,
    marginTop: 10,
    textAlign: 'center',
    width: 250
  },
  container: {

  },
  input: {
    borderColor: 'black',
    borderWidth: 1,
    height: 37,
    width: 250
  }
})

好了,我们已经有了表单,看起来和十亿美元级别的app差不多.下面我们需要使用reduxForm包装器把表单连接到redux form上.这是因为在表单中的每一个按键操作都会发送输入字段的值到store.当我们按下提交按钮,redux-form将会从store中获取保存的值发送到一个我们定制的回调函数.

 import { reduxForm } from 'redux-form'

const submit = values => {
  console.log('submitting form', values)
}

const Form = props => {
  const { handleSubmit } = props

  return (
    <View style={styles.container}>
      <Text>Email:</Text>
      <TextInput style={styles.input} />
      <TouchableOpacity onPress={handleSubmit(submit)}>
        <Text style={styles.button}>Submit</Text>
      </TouchableOpacity>
    </View>
  )
}

export default reduxForm({
  form: 'test'
})(Form)

注释:为了简单,省略了样式和对象的导入代码

好了,首先我们使用reduxForm包装form并连接到store.这是react-redux的基础版本之上的一点修改,可能你比较熟悉了.

接着,我们使用了redux-form的handleSubmit(这个函数是通过redux-Form注入到组件中的).submit函数附加到我们的submit按钮上,所以点击按钮的时候,表单会被提交.这一点和web开发的submit函数是不同的,web开发中这个函数附加到form元素上.在移动平台上没有form元素,所以我们直接把它添加到button上,或者TouchableOpaticy.就是这么回事.

到了这一步,可以使用模拟器运行代码试试.我高度推荐react-native-debugger作为Debugger.你可以看看React Native 有关degugging文档的建议.

当你在模拟器中试着点击提交表单时,你可以看到回调函数返回了空值.

值在哪里呢?

3.使用Field包装器把fields连接到store

所以说,redux-form要工作,你必须要使用Field包装器把每一个field连接到store.

 import { Field, reduxForm } from 'redux-form'

const submit = values => {
  console.log('submitting form', values)
}

const renderInput = ({ input: { onChange, ...restInput }}) => {
  return <TextInput style={styles.input} onChangeText={onChange} {...restInput} />
}

const Form = props => {
  const { handleSubmit } = props

  return (
    <View style={styles.container}>
      <Text>Email:</Text>
      <Field name="email" component={renderInput} />
      <TouchableOpacity onPress={handleSubmit(submit)}>
        <Text style={styles.button}>Submit</Text>
      </TouchableOpacity>
    </View>
  )
}

export default reduxForm({
  form: 'test'
})(Form)

注意,我们添加了Field组件,给定了name属性,和web开发中的inputfield工作是类似的.我们也添加了一个渲染函数告诉reduxForm,这个字段应该怎么渲染.(基本上就是一个TextInput组件).

现在这里有些小技巧,很多人可能会出错.所以留心一下.在web Reac中input组件中,当field的值发生变化时,触发一个onChange回调函数.在React Native中TextInput组件触发的是onChangeText回调函数.为了应对这个问题,我们手动添加变化的句柄onChangeText={onChange}.


现在当我们提交的时候,表单工作了.

使用Immutable.js时候也可以工作

如果你想在state的管理中使用immutable.js,那么需要一些额外的步骤了配合redux-form工作.我建议你读读使用immutable.js和redux-form的官方文档.但是我们通过一些步骤马上开始.

1.使用redux-immutablejs combineReducers和redux-form reducer的immutable版本

找到你穿件redux store的代码处

import { combineReducers } from 'redux-immutablejs'
import { reducer as form } from 'redux-form/immutable' // <--- immutable import

const reducer = combineReducers({ form })

export default reducer

这里有两件事:(1)你必须使用从redux-immutable或者redux-immutablejs注入combineReducers方法.重要的是输入的reducer是从redux-form/immutable.并不是redux-form.

2.使用reduxForm包装器和Field的immutable版本

这一步和第一步实际有点类似.当你在reduxForm中包装一个表单连接到redux store的时候,确保是从redux-form/immutable导入的.类似
Field也是从这里导入的.

import { Field, reduxForm } from 'redux-form/immutable' // <---- LOOK HERE

const submit = values => {
  console.log('submitting form', values.toJS()) <--- use toJS() to cast to plain object
}

const renderInput = ({ input: { onChange, ...restInput }}) => {
  return <TextInput style={styles.input} onChangeText={onChange} {...restInput} />
}

const Form = props => {
  const { handleSubmit } = props

  return (
    <View style={styles.container}>
      <Text>Email:</Text>
      <Field name="email" component={renderInput} />
      <TouchableOpacity onPress={handleSubmit(submit)}>
        <Text style={styles.button}>Submit</Text>
      </TouchableOpacity>
    </View>
  )
}

export default reduxForm({
  form: 'test'
})(Form)

3.完成了

就这些内容,是不是很简单?

加点样式组件让我们的app看起来像十亿美元级别的app

使用绝对酷炫的React样式库styled-components .
看看源代码esbenp/react-native-clean-form

第一步:安装react-native-clean-form

使用npm install —save react-native-clean-form安装form元素.
也需要vector icon fonts.Readme

第二步:设计酷炫的表单

看看代码

 import React, { Component } from 'react'
import {
  ActionsContainer,
  Button,
  FieldsContainer,
  Fieldset,
  Form,
  FormGroup,
  Label,
  Input,
  Select,
  Switch
} from 'react-native-clean-form'

const countryOptions = [
  {label: 'Denmark', value: 'DK'},
  {label: 'Germany', value: 'DE'},
  {label: 'United State', value: 'US'}
]

const FormView = props => (
  <Form>
    <FieldsContainer>
      <Fieldset label="Contact details">
        <FormGroup>
          <Label>First name</Label>
          <Input placeholder="John" />
        </FormGroup>
        <FormGroup>
          <Label>Last name</Label>
          <Input placeholder="Doe" />
        </FormGroup>
        <FormGroup>
          <Label>Phone</Label>
          <Input placeholder="+45 88 88 88 88" />
        </FormGroup>
        <FormGroup>
          <Label>First name</Label>
          <Input placeholder="John" />
        </FormGroup>
      </Fieldset>
      <Fieldset label="Shipping details" last>
        <FormGroup>
          <Label>Address</Label>
          <Input placeholder="Hejrevej 33" />
        </FormGroup>
        <FormGroup>
          <Label>City</Label>
          <Input placeholder="Copenhagen" />
        </FormGroup>
        <FormGroup>
          <Label>ZIP Code</Label>
          <Input placeholder="2400" />
        </FormGroup>
        <FormGroup>
          <Label>Country</Label>
          <Select
              name="country"
              label="Country"
              options={countryOptions}
              placeholder="Denmark"
          />
        </FormGroup>
        <FormGroup border={false}>
          <Label>Save my details</Label>
          <Switch />
        </FormGroup>
      </Fieldset>
    </FieldsContainer>
    <ActionsContainer>
      <Button icon="md-checkmark" iconPlacement="right">Save</Button>
    </ActionsContainer>
  </Form>
)

export default FormView

如果你比较熟悉Twitter Bootstrap你可以从react-native-clean-form中看到类似的句法.好了输入Input,SelectSwitch.这里的元素已经被包装在FromGroupLabel.此外我们也支持验证

import React, { Component } from 'react'
import { reduxForm } from 'redux-form/immutable'
import {
  ActionsContainer,
  Button,
  FieldsContainer,
  Fieldset,
  Form
} from 'react-native-clean-form'
import {
  Input,
  Select,
  Switch
} from 'react-native-clean-form/redux-form-immutable'
import { View,Text } from 'react-native'

const onSubmit = (values, dispatch) => {
  return new Promise((resolve) => {
    setTimeout(() => {
      console.log(values.toJS())
      resolve()
    }, 1500)
  })
}

const countryOptions = [
  {label: 'Denmark', value: 'DK'},
  {label: 'Germany', value: 'DE'},
  {label: 'United State', value: 'US'}
]

class FormView extends Component {
  render() {
    const { handleSubmit, submitting } = this.props

    return (
      <Form>
        <FieldsContainer>
          <Fieldset label="Contact details">
            <Input name="first_name" label="First name" placeholder="John" />
            <Input name="last_name" label="Last name" placeholder="Doe" />
            <Input name="email" label="Email" placeholder="something@domain.com" />
            <Input name="telephone" label="Phone" placeholder="+45 88 88 88 88" />
          </Fieldset>
          <Fieldset label="Shipping details" last>
            <Input name="address" label="Address" placeholder="Hejrevej 33" />
            <Input name="city" label="City" placeholder="Copenhagen" />
            <Input name="zip" label="ZIP Code" placeholder="2400" />
            <Select
              name="country"
              label="Country"
              options={countryOptions}
              placeholder="Denmark"
            />
            <Switch label="Save my details" border={false} name="save_details" />
          </Fieldset>
        </FieldsContainer>
        <ActionsContainer>
          <Button icon="md-checkmark" iconPlacement="right" onPress={handleSubmit(onSubmit)} submitting={submitting}>Save</Button>
        </ActionsContainer>
      </Form>
    )
  }
}

export default reduxForm({
  form: 'Form',
  validate: values => {
    const errors = {}

    values = values.toJS()

    if (!values.first_name) {
      errors.first_name = 'First name is required.'
    }

    if (!values.last_name) {
      errors.last_name = 'Last name is required.'
    }

    if (!values.email) {
      errors.email = 'Email is required.'
    }

    return errors
  }
})(FormView)

很简单,是吗?现在我们有了一个连接到store的好看的表单,还能支持数据验证,异步的按钮反馈.你可以在repository查看更多特性.

附加资料:你可能会有疑问,如果我要提交表单数据到服务器端怎么办呢?
作者在这里没有写出来.如果想了解可以参看这个repo

The End

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

推荐阅读更多精彩内容