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
大体的步骤是:
- 看看redux-form在native和web之间的差异点
- 看看怎么使redux-form和Immutable.js一起使用
- 使用react-native-clean-form的完整实例
鸡冻不?好了,开始了
在React-native中使用redux-form
如果你不太熟悉redux-form,我建议你看看redux-form简单文档
.注意:这个指南假定你使用的redux-form的版本大于6.4.2.如果不是这个版本,并且还在使用Immutable.js,那么要看看这个issue.创建一个表单基础的三个步骤是:
- 在你的redux store中加入redux-form reducer
- 使用
reduxForm
包装器把你的表单连接到store - 使用
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开发中的input
field工作是类似的.我们也添加了一个渲染函数告诉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
,Select
和Switch
.这里的元素已经被包装在FromGroup
和Label
.此外我们也支持验证
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