mapPropsToFields
,onValuesChange
formBinding
,這個容器能夠爲表單指定初始值,也能夠接受到表單值變動去更新本身的狀態。
formBinding
已經綁定好的數據同步給使用表單的組件<DEMO />
。
forBinding
發生數據變化時,發佈一個事件去通知訂閱這個事件的組件去用表單的數據更新本身的state
watch
去作這件事,這樣就不用手動的監聽事件了。
<DEMO />
state中的formScope中,算是約定吧。
form
沒有給出他包裹組件的引用,可是,看他的源碼後發現,在rc-form中能夠直接經過wrappedcomponentref
來拿到包裹組件的引用,連接
watch
的,能夠直接在formBinding
中完成state的綁定
import { Form, Input, Tooltip, Icon, Cascader, Select, Row, Col, Checkbox, Button, AutoComplete, } from 'antd'; const FormItem = Form.Item; const Option = Select.Option; // 簡單的eventemit,在實際項目中使用成熟的第三方組件 const isFunction = function(obj) { return typeof ojb === 'function' || false; }; class EventEmitter { constructor() { this.listeners = new Map(); } addListener(label, callback) { this.listeners.has(label) || this.listeners.set(label, []); this.listeners.get(label).push(callback); } removeListener(label, callback) { let listeners = this.listeners.get(label); let index; if (listeners && listeners.length) { index = listeners.reduce((i, listener, index) => { return isFunction(listener) && listener === callback ? (i = index) : i; }, -1); } if (index > -1) { listeners.splice(index, 1); this.listeners.set(label, listeners); return true; } return false; } emit(label, ...args) { let listeners = this.listeners.get(label); if (listeners && listeners.length) { listeners.forEach(listener => { listener(...args); }); return true; } return false; } } class Observer { constructor(subject) { this.subject = subject; } on(label, callback) { this.subject.addListener(label, callback); } } let observable = new EventEmitter(); let observer = new Observer(observable); //############################################################## // 雙向綁定的表單的數據 const formBinding = WrappedComponent => { return class extends React.Component { state = { scope: {}, }; onFormChange = values => { console.log('form change'); console.log(values); console.log(this.state.scope); const tempScope = Object.assign({}, this.state.scope); this.setState( { scope: Object.assign(tempScope, values), }, () => { // 發送同步實際組件的事件 observable.emit('syncFormState', this.state.scope); }, ); }; render() { return ( <WrappedComponent scope={this.state.scope} onFormChange={this.onFormChange} /> ); } }; }; // 監聽事件,將表單的數據同步到實際組件的state上 const watcher = Component => { return class extends React.Component { componentDidMount() { observer.on('syncFormState', data => { this.handleSyncEvent(data); }); } handleSyncEvent(data) { this.node.setState({ formScope: Object.assign({}, data), }); } render() { return <Component ref={node => (this.node = node)} {...this.props} />; } }; }; @formBinding @Form.create({ mapPropsToFields(props) { // 使用上層組件的scope的值做爲表單的數據 const { scope } = props; return { nickname: Form.createFormField({ value: scope.nickname, }), phone: Form.createFormField({ value: scope.phone, }), address: Form.createFormField({ value: scope.address, }), agreement: Form.createFormField({ value: scope.agreement, }), }; }, onValuesChange(props, values) { // 將表單的變化值回填到上層組件的scope中 props.onFormChange(values); }, }) @watcher // 接受事件去更新state class Demo extends React.Component { state = { formScope: {}, }; handleSubmit = e => { e.preventDefault(); this.props.form.validateFieldsAndScroll((err, values) => { if (err) { console.log('Received values of form: ', values); } console.log('value'); console.log(values); }); }; render() { const { getFieldDecorator } = this.props.form; const { autoCompleteResult } = this.state; const formItemLayout = { labelCol: { xs: { span: 24 }, sm: { span: 6 }, }, wrapperCol: { xs: { span: 24 }, sm: { span: 14 }, }, }; const tailFormItemLayout = { wrapperCol: { xs: { span: 24, offset: 0, }, sm: { span: 14, offset: 6, }, }, }; const prefixSelector = getFieldDecorator('prefix', { initialValue: '86', })( <Select style={{ width: 60 }}> <Option value="86">+86</Option> <Option value="87">+87</Option> </Select>, ); return ( <Form onSubmit={this.handleSubmit}> <FormItem {...formItemLayout} label={<span>Nickname</span>} hasFeedback> {getFieldDecorator('nickname', { rules: [ { required: true, message: 'Please input your nickname!', whitespace: true, }, ], })(<Input />)} </FormItem> <FormItem {...formItemLayout} label="Phone Number"> {getFieldDecorator('phone', { rules: [ { required: true, message: 'Please input your phone number!' }, ], })(<Input addonBefore={prefixSelector} style={{ width: '100%' }} />)} </FormItem> {this.state.formScope.nickname && this.state.formScope.phone ? ( <FormItem {...formItemLayout} label="Address"> {getFieldDecorator('address', { rules: [{ required: true, message: 'Please input your address' }], })(<Input style={{ width: '100%' }} />)} </FormItem> ) : null} <FormItem {...tailFormItemLayout} style={{ marginBottom: 8 }}> {getFieldDecorator('agreement', { valuePropName: 'checked', })( <Checkbox> I have read the agreement </Checkbox>, )} </FormItem> <FormItem {...tailFormItemLayout}> <Button type="primary" htmlType="submit"> Register </Button> </FormItem> <pre>{JSON.stringify(this.state.formScope,null,2)}</pre> </Form> ); } } ReactDOM.render(<Demo />, mountNode);
import { Form, Input } from 'antd'; import _ from 'lodash' const FormItem = Form.Item; // 監聽表單的變化,同步組件的state const decorator = WrappedComponent => { return class extends React.Component { componentDidMount() { const func = this.node.setFields Reflect.defineProperty(this.node, 'setFields', { get: () => { return (values, cb) => { this.inst.setState({ scope: _.mapValues(values, 'value'), }) func(values, cb) } } }) } render() { console.debug(this.props) return <WrappedComponent wrappedComponentRef={inst => this.inst = inst} ref={node => this.node = node} {...this.props} /> } } } @decorator @Form.create({ mapPropsToFields(props) { return { username: Form.createFormField({ ...props.username, value: props.username.value, }), }; }, }) class DemoForm extends React.Component { state = { scope: {}, } render() { const { getFieldDecorator } = this.props.form; return ( <Form layout="inline"> <FormItem label="Username"> {getFieldDecorator('username', { rules: [{ required: true, message: 'Username is required!' }], })(<Input />)} </FormItem> <pre className="language-bash"> {JSON.stringify(this.state.scope, null, 2)} </pre> { this.state.scope.username ? <FormItem label={<span>address</span>}> {getFieldDecorator('address', { rules: [ { required: true, message: 'Please input your address!', whitespace: true, }, ], })(<Input />)} </FormItem> : null } </Form> ); } } class Demo extends React.Component { state = { fields: { username: { value: 'benjycui', }, }, }; handleFormChange = (changedFields) => { this.setState(({ fields }) => ({ fields: { ...fields, ...changedFields }, })); } render() { const fields = this.state.fields; return ( <div> <DemoForm {...fields} /> </div> ); } } ReactDOM.render(<Demo />, mountNode);