redux form

純粹使用react進行表單校驗:html

class MyForm extends React.Component{ constructor(props){ super(props) this.onAddrChange = this.onAddrChange.bind(this); this.state = { addr:"" } } onAddrChange(evt){
        this.setState({ addr:evt.target.value }) } render(){ return ( <form>
                <input type="text" value={this.state.addr} onChange={this.onAddrChange}/>
            </form>
 ) } }

可見爲了維持雙向綁定,以及校驗信息。一個input至少須要3個以上的變量(value + change callback + verify message),表單比較大的話,代碼邏輯十分複雜。前端

 

redux-form-utils這個庫沒作好,內部報錯了用不了。直接使用redux-formreact

redux-form

https://redux-form.com/6.6.3/examples/redux

最簡單的例子:api

import React from 'react'; import ReactDOM from 'react-dom'; import {createStore, combineReducers} from 'redux' import { Provider } from 'react-redux' import {reducer, Field, reduxForm} from "redux-form" const rootReducer = combineReducers({ form: reducer }); const store = createStore(rootReducer); class ContactForm extends React.Component{ handleSubmit(e){ console.log("submited") e.preventDefault() } render(){ return ( <form onSubmit={this.handleSubmit}>
                <div>
                    <label htmlFor="email">Email</label>
                    <Field name="email" component="input" type="text" />
                </div>
                <button type="submit">Submit</button>
            </form>
 ) } } ContactForm = reduxForm({ form: 'contact' })(ContactForm) ReactDOM.render( <Provider store={store}>
        <ContactForm store={store}></ContactForm>
    </Provider>,
    document.getElementById('root') );

基本的使用流程:promise

  1. 建立store,使用的redux-form中的redux,這樣這個store就具有了處理redux-form內置action的能力了。
  2. 定義表單組件,在表單中使用Field做爲input(自動把value、onChange等封裝到了input中,用於維持雙向綁定)。
  3. 使用reduxForm加強咱們的表單組件,加強後的表單能夠從context中獲取store,並從store中獲取state,state中的數據經過props傳遞給咱們的表單組件(除了能夠從props中獲取store的state外,還能夠獲取處理表單提交的函數),即便表單具有了與store通訊的能力。
  4. 在加強後的表單組件外部經過provider提供一個store,給裏面的子組件調用。

理解以上的更新關係很重要:服務器

  

 

組件樹中的組件的渲染順序都是深度優先,即 全部的Field會被首先執行,內部執行的過程當中就把name值設置到store上了。框架

Field

這個內置的組件用於加強咱們的input。以上的component屬性中使用的是input。其實也能夠傳遞一個自定義的組件:dom

function MyCs(props){ console.log(props) return <div></div> } <Field name="email" component={MyCs} type="text" x="123"/>

輸出的數據:異步

以上的Field標籤渲染出來的結果就是一個 <div></div>。可見,經過Field組件,咱們能夠封裝一些表單中的子組件。

input和meta中的屬性參考:https://redux-form.com/7.2.0/docs/api/field.md/#props

表單校驗

查看以上的關係圖能夠發現,onChange是定義在加強後的組件中的,在那裏能夠最先獲取到表單數據,也能夠把校驗相關的函數封裝進去。由於這個加強後的組件是經過reduxForm生成的,天然而然校驗相關的函數也應該經過reduxForm傳遞進去(經過一個配置對象傳入):

export default reduxForm({ form: 'syncValidation', validate, // <--- validation function given to redux-form
  warn // <--- warning function given to redux-form
})(SyncValidationForm)

 

以上函數中能夠經過參數獲取到最新的表單數據,返回一個校驗信息對象,這個對象中映射了指定name的input與之校驗信息的對應關係。

使用的方式以下:

校驗函數(error|warning)返回  { age: "age is required" }
對於組件:<Field name="age" type="number" component={renderField} label="Age" /> 。在renderField這個組件內,能夠經過參數props.meta.error|warning獲取到 "age is required" 這個校驗信息,而後組件內把這個錯誤信息顯示出來便可。

error和warning函數的返回對象均可以用在Field所指定的組件內部,但他們的區別是什麼?只要error有值,則表單onSubmit就不會觸發,而warning不影響提交。

Field級別的校驗

以上的校驗是針對整個表單的校驗,全部校驗邏輯集中在一個函數中。使用Field級別的校驗能夠把每種校驗類型拆分出來,針對Field可實現更加靈活的校驗控制。

const required = value => (value ? undefined : 'Required') const alphaNumeric = value => value && /[^a-zA-Z0-9 ]/i.test(value) ? 'Only alphanumeric characters' : undefined <Field name="username" type="text" component={renderField} label="Username" validate={[required, maxLength15, minLength2]} warn={alphaNumeric} />

 返回undefined則表明校驗經過,返回的字符串和上面同樣,能夠在對應的Field指定的組件內經過props.meta.warning | error 獲取。

異步校驗

reduxForm加強表單的時候配置對象以下:

export default reduxForm({ form: 'asyncValidation', validate, asyncValidate, asyncBlurFields: ['username'] })(AsyncValidationForm)

asyncBlurFields:指定當哪些FieldonBlur的時候觸發異步校驗

asyncValidate:指定異步校驗的函數,在這個函數中返回一個promise,promise中若是拋出一個校驗信息對象,則表明校驗失敗

服務器端校驗

以上輸出前端的校驗,怎麼處理服務端的校驗信息呢?

在submit的回調函數中,返回一個promise,在這個promise中進行異步請求。這個promise被resolve的話則提交完成。若是內部拋出SubmissionError錯誤,則表明校驗失敗

throw new SubmissionError({ password: 'Wrong password', _error: 'Login failed!' })

 被包裹的對象和以前校驗函數返回的對象用法一致。

表單狀態的初始化

把reduxForm加強後的表單,進行connect;而表單中Field字段,默認讀取props.initialValues.xxname的值,因此簡單的同步初始化方式是:

const data = { firstName: 'Jane', lastName: 'Doe', } InitializeFromStateForm = connect( state => ({ initialValues: data }) )(InitializeFromStateForm)

由於connect的做用是實現自動關聯。因此只要在異步請求後,調用dispatch,把異步數據發送給reducer,上面就不用直接讀取data,而是從state中讀取異步數據,界面能自動刷新,這樣就實現了異步的狀態初始化,簡單的例子能夠查看 這裏

注意:由於props會層層傳遞,因此,以上connect中mapStateToProps(state)和mapDispatchProps(dispatch)返回的值也能夠在表單中經過props.xxx來使用,針對這一點,容許咱們在表單中使用一些表單值之外的變量

獲取處理表單數據

假如我有一個需求,當點擊了checkBox,則把一個表單模塊顯示出來。由於表單項的值都交由框架進行處理,因此沒辦法在jsx中直接訪問。但經過connect映射出來的props能夠在jsx中直接使用,咱們只須要在映射的時候獲取到表單項的值,而後對這些值進行映射,把新的值返回。這樣就能夠在jsx中訪問了。經過redux-form提供的selector能夠獲取到表單項的值。

FieldArray

用於相同輸入組件的動態添加和刪除。

表單值的格式化

對用戶輸入的值進行格式化,格式化後的值能夠在onSubmit中獲取,以及會顯示到界面上。使用方式和Field級別的校驗相似,在Field上添加一個字段,指定一個格式化函數便可。

Immutable 

將程序的狀態所有設置爲不可變,能夠把從redux-form獲取的值所有改爲從redux-form/immutable中獲取,校驗以及提交的時候,獲取值從values.xxx 更改成 values.Get("xxx") 便可。

嚮導型的表單

把表單拆分紅多個部分,每一個部分放在不一樣的頁面中顯示,每一個頁面填寫完,再跳轉到下一個頁面繼續填寫,相似於嚮導頁。

以上說切換頁面,實際是在切換組件的顯示,每一個組件表明一個表單頁,內部有一個完成的表單,但在redux-from加強的時候必須指定這多個表單的具備相同的form值,並且unmount的時候不銷燬。

相關文章
相關標籤/搜索