這是 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() } ... }
使用過 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 組件會自動爲你更新所綁定的 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 屬性目前爲實驗特性,若是發如今某個瀏覽器或某種輸入法中有問題,歡迎提交 issue 。
每一個 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 是一個單選框選項組,只要傳入選項數組 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 是一個選擇框組件,使用方法與 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 的高階組件均是使用 Typescript 進行開發 , 提供完整的類型定義文件,當你在 javascript 項目使用它們時,也能夠獲得良好的組件屬性 intelliSense 類型提示:
你也能夠右鍵點擊組件名,選擇轉到類型定義
, 查看組件的全部屬性聲明:
上圖的 type 屬性的類型是枚舉字符串,在輸入時,你能夠在空雙括號中按下 vsCode 的 「觸發提示」 快捷鍵(具體快捷鍵因不一樣系統操做系統和不一樣設置而異,請到編輯器的 「首選項->設置快捷方式」 處查看,該功能很實用):
不少時候,咱們會使用 react 視圖組件庫來開發應用,如 ant.design、 material-ui 等,pastate 爲這些現有的視圖組件提供兩個數據雙向綁定方法!下面咱們以 ant.design 的 Rate 星級評分組件爲例進行介紹。
你可使用 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 組件的星星或點擊 descrease age
,increase age
按鈕對年齡值進行改變,能夠發現當經過按鈕更新數據時,Rate 組件能夠正確響應。並且 Bind 是一個關於 value 的純組件,當其餘無關數據發生改變時,Bind 元素不會發生多餘的渲染動做。
Bind 元素還有兩個可選的屬性:
value
, 絕大多數組件也是用 value
,所以無需指定該值。若是被包裝的組件使用的是 checked
或其餘屬性名接收數據值,那麼請把 Bind 元素的 valueProp 設爲對應的 checked
或其餘屬性名。(newValue) => void
。當綁定的屬性值不是經過該組件發生改變時,afterChange 函數不會被調用。你也可使用 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 應用如何進行模塊化。