Part1 双向绑定
官方是不推崇双向绑定的,好用是真好用,不过我们先明确几个原则
- 举个例子,我们有 "编辑组件TextInput" 和一个"页面class Page",数据落在Page.state.a.b.c.d里
- 绑定的set/get只能在"更新-显示"链的最末端,也就是说当TextInput实现双向绑定以后,TextInput其内部就没有用户代码了
- 初衷是绑定,数据当然就落在远端Page,通过给props方式把数据递给TextInput
- 为保证显示一致,须达到
<TextInput value={this.state.a.b.c.d} />
`这种效果 - 为了更新 TextInput要调用
page.setState()
不用考虑琐碎的的性能问题,使用ImmutableJS (我用的便携版Seamless-Immutable)
// path[0] = this // return path[0].state.path[1].path[2]...; let getValue = (path) => { let obj = path[0].state; for (let i=1; i<path.length; i++) { if (path[i] in obj) obj = obj[path[i]]; else return null; } return obj; } // path[0] = this // path[0].state.path[1].path[2]... = newvalue let setValue = (path, newvalue) => { let laststate = SImmutable(path[0].state[path[1]]); let nextstate; if (path.length > 2) nextstate = laststate.setIn(path.slice(2), newvalue); else nextstate = newvalue; let obj={}; obj[path[1]] = nextstate; // console.log(obj); path[0].setState(obj); } export function EphText (props) { return ( <input type="text" {...props} style={my_style} value = {getValue(props.ephbind)} onChange={ e => {setValue(props.ephbind, e.target.value);} } /> ); }
Part2 存储好看 还是 用户开发友好
我们想实现如下功能:
- 最后一行作为空行,当用户修改当前行时候,在后面新增一个空行
- 删除中间一行,不引起其他行reRender
- 提交数据时候比较容易读到直接submit
- 表格使用[row1:{}, row2:{}, ...]这种方式而不是二维数组
先来一个:类似Java的无所不包Class模式
- 最容易想到
- 建立Storage类,存储表格在state中的path
- 这种思想源自统筹一切的class,关于表格数据的一切操作都被class覆盖
- 当然数据格式还是用户自己管理,是字符串还是object,StorageClass不关心
我感觉代码太长,因为StorageClass写死开发者不可控,不能一两句能说清楚,不能一眼看懂数据变化关系和效率
使用时候大致如下:export class Storage { constructor(path, rowtemplate) {} set(r, c, newval) {} //set value rowHash() {} //get row hash get(r, c) {} //get value rmRow(r) {} //remove a row renderArray(); //array for render serialize(); //tojson } ptr = new Storage([path]); <MyTable title=['col1', 'col2']> {ptr.renderArray().map((row, index) => <tr key={ptr.rowHash(index)}> <td><TextInput bind={ptr, row} /></td> <td><TextInput value={ptr.get(row, 2)} onChange={e=>{ptr.set(row, 2, e.target.value);}} /></td> </tr> )} </MyTable>
第二个问题,实现删除一行其他行不渲染?
- 当然通过key规避,如果key是数组index,那删了肯定会重新渲染
- 如果数组是数据hash,需要证明unique
- 我的方案是把删掉的行设为null,数组位置还占着,渲染时跳过
下一个问题,想实现“最后一行作为新增功能”,那这个空行 要不要进入Page.state.mytable
存储
- 存储:渲染省事,当onChange最后一行每个元素时 自动copy&push
- 但是存储要在Ajax.fetch()后就遍历所有数据点,提交前还要把最后一行清了
- 不存储:Table就要想个办法多渲染一行
我的一个办法,取个巧
Page.state.mytable
不存储最新一行的数据,而改用tabletmpl
变量存储待插入行的模板- 将待插入行渲染时加入渲染数组,带key排序
尽量不违背一眼看穿原则,看下面代码就明白了
const table2_tmpl = {name1:"", name2:""}; <MyTable title={['name1', 'name2']}> {[...this.state.demotable, table2_tmpl].map( (row, index) => { if (row == null) return; let islastrow = (index==this.state.demotable.length?row:index); return ( <tr key={index}> <td>{index+1}</td> <td><TextInput bind={[this, 'demotable', islastrow, 'name1']} /></td> <td><TextInput bind={[this, 'demotable', islastrow, 'name2']} /></td> <td></td> <td><DeleteButton bind={[this, 'demotable', index]}/></td> </tr> ); })} </MyTable>
11