【教程】Pastate.js 響應式 react 框架(四)表單渲染與操做

這是 Pastate.js 響應式 react state 管理框架系列教程的第四章,歡迎關注,持續更新。Pastate.js Githubjavascript

這一章,咱們來看看如何在 pastate 裏渲染和操做表單元素。css

使用原生表單元素

咱們在 BasicInfoView 組件的兩個按鈕下面添加一個輸入框用於輸入姓名,並添加一個勾選框用於選擇性別,更改以下:vue

class BasicInfoView extends PureComponent {
    render() {
        ...
        return (
            <div style={{ padding: 10, margin: 10 }}>
                ...
                <div>
                    <button onClick={this.decreaseAge}> decrease age </button>
                    <button onClick={this.increaseAge}> increase age </button>
                </div>
                <div>
                    name: <input type="text" value={state.name} onChange={this.handleNameChange}/> <br />
                    Is boy: <input type="checkbox" checked={state.isBoy == true} onChange={this.handleIsBoyChange}/>
                </div>
            </div>
        )
    }
}

上面添加兩個了 input 標籤,第一個 input 使用 name 數據, 第二個 input 使用 isBoy 數據。同時咱們也先指定兩個 input 的 onChange 處理函數。
注意:如前面章節所說起,對於 imState 布爾值,請記得使用顯式布爾值方式:checked={state.isBoy == true}java

接下來看看如何實現兩個 onChange 處理函數:react

class BasicInfoView extends PureComponent {
    ...
    handleNameChange(e){
        state.basicInfo.name = e.target.value
        store.sync() // 編輯中的輸入框,需手動同步store
    }
    handleIsBoyChange(e){
        state.basicInfo.isBoy = e.target.checked
    }
    ...
}

很是熟悉和簡單!你只需把更新的值賦給目標state節點便可。咱們會發現多了個 store.sync() ,這個函數是讓 pastate 馬上執行數據同步更新。因爲不少輸入法會在輸入過程當中會把帶下劃線的 「拼音字母」 輸入到 input 中,以下: git

把拼音值輸入文本框的輸入法

若是 pastate 執行異步更新會使帶下劃線 「拼音字母」 中斷,所以咱們在更新 編輯中的輸入框 時,須要簡單地使用 store.sync() 執行同步更新,讓 「拼音」 連續。若是不是對 編輯中的輸入框 進行修改或不須要支持輸入法輸入(如密碼等),無需使用 store.sync() github

初步完成!咱們試着往輸入框輸入文字,或點擊勾選框, 能夠看到文字區域的值與 input 區域的視圖都正常地進行更新!npm

與普通 react 表單的處理模式同樣,咱們能夠經過 onChange 函數的實時控制輸入的內容,如轉化大小寫、控制字符串長度等:segmentfault

class BasicInfoView extends PureComponent {
    ...
    // 把輸入值都轉化爲大寫
    handleNameChange_uppercase(e){
        state.basicInfo.name = e.target.value.toUpperCase()
        store.sync()
    }

    // 控制文本長度在10個字符之內
    handleNameChange_limitedLength(e){
        if(e.target.value.length > 10) return;
        state.basicInfo.name = e.target.value
        store.sync()
    }
    ...
}

使用 pastate 雙向綁定輸入組件

使用過 vue.js 或 angular.js 的人都體驗過自動雙向數據綁定 (two-ways data binding) 的便捷性,但因爲 react state 渲染的單向數據流原則,在 react 中沒有默認提供這個功能。而 pastate 的 imState 具備保存節點信息的特殊性,可以很容易實現自動雙向數據綁定功能! 數組

pastate 爲你提供了四個已實現雙向數據綁定的高階表單組件 (Higher Order Component, 一般簡稱 HOC) ,這些組件都是基於 PureComponent 實現的,具備良好的渲染性能,你能夠盡情享用他們!這四個表單組件以下:

  • Input : 文本框
  • Checkbox : 複選框
  • RadioGroup : 單選框選項組
  • Select : 下拉選擇框

Input 文本框

Input 組件能夠用於顯示單行輸入框和多行輸入框,只須要傳入要綁定的值,當你經過界面更改輸入框的值時,Input 組件會自動爲你更新所綁定的 state :

import { ..., Input } from 'pastate'
...
render(){
    let state = this.props.state
    return(
        ...
        <Input value={state.text1} /> {/** 單行輸入框,內部使用 <input type="text" /> 實現 */}
        <Input value={state.text2} type="textarea" /> {/** 多行輸入框,內部使用 <textarea /> 實現 */}
        ...
    )
}
...

Input 組件的屬性及其說明以下:

屬性 | 值 | 說明

value | string | number , 必填 | 綁定的值,須要使用 this.props.state 中的節點 , 即 imState
type | "text" | "textarea" | "password" | "number" | 輸入框類型,默認爲 "text"
beforeChange | (newValue: string | number, oldValue: string | number) => string | number | 在綁定值更新前會被調用,可用於實現自定義字符串更新邏輯, 如控制大小寫或限制字符串長度等;返回值爲最終要更新的值
afterChange | (newValue: string | number) => void | 在綁定值更新後會被調用
disabled | boolean | 指定輸入框是否處於禁止狀態,默認爲 false
className | string | 傳遞給輸入框的 class 名 ( 用於指定 css 樣式等 )
id | string | 傳遞給輸入框的 id 名 ( 用於指定 css 樣式等 )
useComposedValue | boolean | [ 實驗特性 ] 指定是否在輸入法完成拼音過程時才更新 state 的模式,開啓後,在輸入拼音的過程當中綁定的值不會更新,默認爲 false

Input 組件實現了自動綁定數據雙向綁定功能,若是你須要自定義字符串更新邏輯,或者在字符值更新後作一些操做,能夠經過指定可選的 beforeChange 函數和 afterChange 函數來實現。

...

handleTextBeforeChange(newValue, oldValue) {
    // 把輸入的字符轉化爲大寫
    return newValue.toUpperCase()
}

render(){
    let state = this.props.state
    return(
        ...
        <Input value={state.text1} beforeChange={this.handleTextBeforeChange} /> 
        ...
    )
}
...

Input 組件的屬性 useComposedValue 開啓後,能夠實如今輸入法輸入拼音的過程當中綁定的值不會更新的功能,等拼音輸入完成後 state 值才更新,減小拼音輸入過程當中沒必要要的 state 更新和視圖渲染動做:

useComposedValue 效果

useComposedValue 屬性目前爲實驗特性,若是發如今某個瀏覽器或某種輸入法中有問題,歡迎提交 issue

Checkbox 複選框

每一個 Checkbox 是一個勾選框組件,只需傳遞要綁定的布爾值 state 節點,便可完成綁定:

import { ..., Checkbox } from 'pastate'
...
render(){
    let state = this.props.state
    return(
        ...
        I am a boy: <Checkbox value={state.isBoy} />
        ...
    )
}
...

複選框

Checkbox 組件的屬性及其說明以下:

屬性 | 值 | 說明

value | boolean,必填 | 綁定的數據值,可直接傳入 this.props.state 中的節點值,無需state.xxx == true 轉化。
afterChange | (newValue: boolean) => void | 在綁定值更新後會被調用
disabled | boolean | 指定禁止點擊狀態,默認爲 false
className | string | 傳遞 class 名 ( 用於指定 css 樣式等 )
id | string | 傳遞 id 名 ( 用於指定 css 樣式等 )

RadioGroup 單選框選項組

RadioGroup 是一個單選框選項組,只要傳入選項數組 options 常數和要綁定的選項值 value, 便可完成綁定:

import { ..., RadioGroup } from 'pastate'
const goodNames = ['Peter', 'Tom', 'Allen']

...
render(){
    let state = this.props.state
    return(
        ...
        Choose a name:  <RadioGroup options={goodNames} value={state.name}/>
        ...
    )
}
...

單選框選項組

RadioGroup 組件的屬性及其說明以下:

屬性 | 值 | 說明

options | Array<string | number | boolean> | Array<{value: string | number | boolean, tag: string, disabled?: boolean}>, 必填 | 選項數據數組
value | string | number | boolean,必填 | 綁定的選中值
disabled | boolean | 指定禁止選擇狀態,默認爲 false
vertical | boolean | 指定選項爲垂直排列狀態,默認爲 false
afterChange | (newValue: string | number | boolean) => void | 在綁定值更新後會被調用
id | string | 傳遞給選項組根元素的 id
className | string | 傳遞給選項組根元素的 className
radioClassName | string | 傳遞給圓形按鈕的 className
tagClassName | string | 傳遞給選項標籤的 className
disabledTagClassName | string | 傳遞給禁用狀態的選項標籤的附加的 className

options 屬性接受兩種格式的選項數據格式:

// 簡單格式: Array<string> 
const nameOptions = ['Peter', 'Tom', 'Allen']

// 完備格式: Array<{value: string, tag: string, disabled?: boolean}>
const nameOptionsChinese = [{
    value: 'Peter', // 數據值
    tag: '彼得', // 選項標籤值
    disabled: true // 可選地指定某個選項爲不可選
},{
    value: 'Tom',
    tag: '湯姆'
},{
    value: 'Allen',
    tag: '艾倫'
}]

RadioGroup 是基於 PureComponent 實現的,對於 options 屬性的值,建議定義一個 文件級 的選項數組常量, 這樣能夠提升渲染效率。若是把 options 值定義在 render 函數裏或寫成直接賦值的匿名對象(<RadioGroup options={["a", "b"]} ... >), 在每次父組件渲染時,不管綁定的 value 數據有沒有更新,RadioGroup 獲取到的 options 屬性的引用值都不同,會使 RadioGroup 進行多餘的渲染動做。 當你使用其餘基於 PureComponent 實現的組件時,在向其傳遞數組 / 對象類型的常量的時候也能夠作這樣的性能優化。

Select 下拉選擇框

Select 是一個選擇框組件,使用方法與 RadioGroup 相似,只要傳入選項數組和要綁定的選擇值便可完成綁定:

import { ..., Select } from 'pastate'

const nameOptionsChinese = [{
    value: 'DEFAULT',
    tag: '請選擇',
    disabled: true
},{
    value: 'Peter',
    tag: '彼得'
}, {
    value: 'Tom',
    tag: '湯姆'
}, {
    value: 'Allen',
    tag: '艾倫'
}]

...
render(){
    let state = this.props.state
    return(
        ...
        Choose a name:  <Select options={nameOptionsChinese} value={state.name}/>
        ...
    )
}
...

下拉選擇框

不一樣操做系統、不一樣瀏覽器的下拉樣式不同

Seclect 組件的屬性及其說明以下:

屬性 | 值 | 說明

options | Array<string | number | boolean> | Array<{value: string | number | boolean, tag: string, disabled?: boolean}>, 必填 | 選項數據數組
value | string | number | boolean,必填 | 綁定的選中值
disabled | boolean | 指定禁止點擊狀態,默認爲 false
afterChange | (newValue: string | number | boolean) => void | 在綁定值更新後會被調用
id | string | 傳遞的 id
className | string | 傳遞的 className

若有須要顯示沒有選擇的狀態,能夠多設置一個選項元素,經過元素的 tag 設置其提示文本, 並把元素的 disabled 設爲 true 便可,沒選中時的 value 值自行定義,不可爲 null 或 undefined:

const nameOptionsChinese = [{
    value: 'DEFAULT',
    tag: '請選擇',
    disabled: true
}, ... ]

Seclect 組件目前僅支持最經常使用的單選功能,之後將支持多選功能。

pastate 表單組件的類型定義

Pastate 的高階組件均是使用 Typescript 進行開發 , 提供完整的類型定義文件,當你在 javascript 項目使用它們時,也能夠獲得良好的組件屬性 intelliSense 類型提示:

組件的屬性類型提示

你也能夠右鍵點擊組件名,選擇轉到類型定義, 查看組件的全部屬性聲明:

選擇轉到類型定義

組件的類型定義文件

上圖的 type 屬性的類型是枚舉字符串,在輸入時,你能夠在空雙括號中按下 vsCode 的 「觸發提示」 快捷鍵(具體快捷鍵因不一樣系統操做系統和不一樣設置而異,請到編輯器的 「首選項->設置快捷方式」 處查看,該功能很實用):

屬性值提示

對現有組件庫進行雙向數據綁定

不少時候,咱們會使用 react 視圖組件庫來開發應用,如 ant.designmaterial-ui 等,pastate 爲這些現有的視圖組件提供兩個數據雙向綁定方法!下面咱們以 ant.design 的 Rate 星級評分組件爲例進行介紹。

使用 Bind 高階組件

你可使用 pastate 提供的 Bind 組件去包圍一個原始的視圖組件,實現雙向數據綁定。
咱們先簡單地安裝 ant.design 組件庫: $ npm install antd --save$ yarn add antd,引入 Rate 組件,並使用 Bind 組件進行包裝,實現雙向數據綁定:

import { ..., Bind } from 'pastate';

import Rate from 'antd/lib/rate';
import 'antd/lib/rate/style/css';
// 或通過簡單配置後,使用 import { Rate } from 'antd', 詳見 ant.design 文檔
...

// 咱們使用 state.basicInfo.age  數據進行演示

class BasicInfoView extends PureComponent {
    ...
    render() {
        /** @type {initState['basicInfo']} */
        let state = this.props.state;
        return (
            <div style={{ padding: 10, margin: 10 }}>
                ...
                <div>
                    年齡(Bind): 
                    <Bind value={state.age} > 
                        <Rate />
                    </Bind>
                </div>
                ...
            </div>
        )
    }
}

咱們使用 Bind 組件對 Rate 組件進行包裝,並把原本須要傳遞給 Rate 組件的 value 值傳遞經過 Bind 組件進行傳遞,這樣就實現了雙向數據綁定!若是咱們要傳遞其餘屬性值給 Rate, 能夠不經過 Bind 直接傳遞給 Rate,以下:

...
<Bind value={state.age} > 
    <Rate count={10} /> {/* 根據 ant design 文檔, count 屬性指定採用 n 級評分 */}
</Bind>
...

這樣咱們就實現了對 Rate 進行雙向數據綁定:

成功綁定 Rate 組件到 age 數據

你能夠經過點擊 Rate 組件的星星或點擊 descrease age ,increase age 按鈕對年齡值進行改變,能夠發現當經過按鈕更新數據時,Rate 組件能夠正確響應。並且 Bind 是一個關於 value 的純組件,當其餘無關數據發生改變時,Bind 元素不會發生多餘的渲染動做。

Bind 元素還有兩個可選的屬性:

  • valueProp: 指定被包裝的組件接收數據的屬性名,默認是 value, 絕大多數組件也是用 value,所以無需指定該值。若是被包裝的組件使用的是 checked 或其餘屬性名接收數據值,那麼請把 Bind 元素的 valueProp 設爲對應的 checked 或其餘屬性名。
  • afterChange: 當組件綁定的值經過該組件發生改變後,會調用該函數,該函數的簽名爲 (newValue) => void。當綁定的屬性值不是經過該組件發生改變時,afterChange 函數不會被調用。

使用 makeBindable 函數

你也可使用 makeBindable(RawComponent, valueProp = 'value') 函數生成一個能夠複用的可綁定組件

import { ..., makeBindable } from 'pastate';
...

// 使用 makeBindable 生成對應的能夠綁定數據的 Rate 組件
const MyRate = makeBindable(Rate)

class BasicInfoView extends PureComponent {
    ...
    render() {
        /** @type {initState['basicInfo']} */
        let state = this.props.state;
        return (
            <div style={{ padding: 10, margin: 10 }}>
                ...
                <div>
                    年齡(makeBindable): 
                    <MyRate count={10} value={state.age}/> 
                </div>
                ...
            </div>
        )
    }
}

你能夠經過 makeBindable 函數的第二個參數指定 valueProp 值,如 const MyCheckbox = makeBindable(Checkbox, 'checked'), 一樣,你能夠經過新組件的 afterChange 屬性去響應組件值更新後的個性化操做。

對於綁定的值爲空的狀況

不管使用 pastate 的表單輸入組件仍是包裝現有的組件庫,對於組件綁定的數據值,不支持被設爲 null 值 或 undefind 值。一般狀況下,咱們不會有這種需求。若是須要實現表單的「未選擇」狀態, 咱們通常經過設置一個默認值且不可選的 default 值來代替。例若有個表單須要選擇性別,而且須要一個未選擇的狀態,這樣使用一個 RadioGroup 組件來實現:

let initState = {
  sex: '' // 用字符串表示性別,並設置爲選擇狀態是的值爲空字符串 '' 或 'default'
}
const sexOptions = ['boy', 'girl'] // 只包含目標值
...
render(){
  let state = this.props.state;
  return <RadioGroup options={sexOptions} value={state.sex} />
}
...

若是你的需求場景必定要用到 null 或 undefined 值,歡迎在 issues 中分享。 Pastate 能實現綁定的值支持空值,但若是這個需求只有在很是特殊的狀況下才用到,咱們就暫不把它默認實如今 pastate 庫中,由於這會增長計算量,你能夠自行實現這個特殊組件的數據綁定邏輯。

下一章,咱們來看看 pastate 應用如何進行模塊化。

相關文章
相關標籤/搜索