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來創(chuàng)建表單.redux-form是一個非常好的庫,但是沒有什么好的示例代碼介紹怎么和React-native配合使用.所以當(dāng)我開始在React-Natvie中使用redux-form的時候,發(fā)現(xiàn)還是有一點(diǎn)難度的.大多數(shù)的情況都因為我發(fā)現(xiàn)在React-Native中使用Immutable.js的時候會中斷redux-form運(yùn)行的bug.Redux-form主版本號已經(jīng)到了6.4.2了.所以我考慮可以根據(jù)我的學(xué)習(xí)過程來寫一點(diǎn)怎么在應(yīng)用中同時使用redux-form和Immutable.js的文檔.
Agenda
大體的步驟是:
- 看看redux-form在native和web之間的差異點(diǎn)
- 看看怎么使redux-form和Immutable.js一起使用
- 使用react-native-clean-form的完整實(shí)例
雞凍不?好了,開始了
在React-native中使用redux-form
如果你不太熟悉redux-form,我建議你看看redux-form簡單文檔
.注意:這個指南假定你使用的redux-form的版本大于6.4.2.如果不是這個版本,并且還在使用Immutable.js,那么要看看這個issue.創(chuàng)建一個表單基礎(chǔ)的三個步驟是:
- 在你的redux store中加入redux-form reducer
- 使用
reduxForm
包裝器把你的表單連接到store - 使用
Field
包裝器把特定的表單字段連接到Store
0. 創(chuàng)建React Native 項目
假設(shè)你已經(jīng)有了可以運(yùn)行的React Native項目.如果沒有,使用react-native init MyReduxFormProjct
終端命令行命令也很容易創(chuàng)建項目.
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
}
})
![](https://ww3.sinaimg.cn/large/006tKfTcly1fe9qllp362j30680b4743.jpg)
好了,我們已經(jīng)有了表單,看起來和十億美元級別的app差不多.下面我們需要使用reduxForm
包裝器把表單連接到redux form上.這是因為在表單中的每一個按鍵操作都會發(fā)送輸入字段的值到store.當(dāng)我們按下提交按鈕,redux-form將會從store中獲取保存的值發(fā)送到一個我們定制的回調(diào)函數(shù).
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)
注釋:為了簡單,省略了樣式和對象的導(dǎo)入代碼
好了,首先我們使用reduxForm
包裝form并連接到store.這是react-redux
的基礎(chǔ)版本之上的一點(diǎn)修改,可能你比較熟悉了.
接著,我們使用了redux-form的handleSubmit
(這個函數(shù)是通過redux-Form注入到組件中的
).submit函數(shù)附加到我們的submit按鈕上,所以點(diǎn)擊按鈕的時候,表單會被提交.這一點(diǎn)和web開發(fā)的submit函數(shù)是不同的,web開發(fā)中這個函數(shù)附加到form元素上.在移動平臺上沒有form元素,所以我們直接把它添加到button上,或者TouchableOpaticy
.就是這么回事.
到了這一步,可以使用模擬器運(yùn)行代碼試試.我高度推薦react-native-debugger
作為Debugger.你可以看看React Native 有關(guān)degugging文檔的建議.
當(dāng)你在模擬器中試著點(diǎn)擊提交表單時,你可以看到回調(diào)函數(shù)返回了空值.
![](https://ww4.sinaimg.cn/large/006tKfTcly1fe9qmb54t8j31aq0e0dh3.jpg)
值在哪里呢棍辕?
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開發(fā)中的input
field工作是類似的.我們也添加了一個渲染函數(shù)告訴reduxForm,這個字段應(yīng)該怎么渲染.(基本上就是一個TextInput
組件).
現(xiàn)在這里有些小技巧,很多人可能會出錯.所以留心一下.在web Reac中input
組件中,當(dāng)field的值發(fā)生變化時,觸發(fā)一個onChange
回調(diào)函數(shù).在React Native中TextInput
組件觸發(fā)的是onChangeText
回調(diào)函數(shù).為了應(yīng)對這個問題,我們手動添加變化的句柄onChangeText={onChange}
.
![](https://ww1.sinaimg.cn/large/006tKfTcly1fe9qmuwabmj31ao0cmdh6.jpg)
現(xiàn)在當(dāng)我們提交的時候,表單工作了.
使用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版本
這一步和第一步實(shí)際有點(diǎn)類似.當(dāng)你在reduxForm
中包裝一個表單連接到redux store的時候,確保是從redux-form/immutable
導(dǎo)入的.類似
Field
也是從這里導(dǎo)入的.
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.完成了
就這些內(nèi)容,是不是很簡單?
加點(diǎn)樣式組件讓我們的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
第二步:設(shè)計酷炫的表單
![](https://ww4.sinaimg.cn/large/006tKfTcly1fe9qnc5uf1j30ku0cdaag.jpg)
看看代碼
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
.這里的元素已經(jīng)被包裝在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)
很簡單,是嗎唁毒?現(xiàn)在我們有了一個連接到store的好看的表單,還能支持?jǐn)?shù)據(jù)驗證,異步的按鈕反饋.你可以在repository查看更多特性.
附加資料:你可能會有疑問蚯姆,如果我要提交表單數(shù)據(jù)到服務(wù)器端怎么辦呢邀桑?
作者在這里沒有寫出來.如果想了解可以參看這個repo