UNPKG

zent

Version:

一套前端设计语言和基于React的实现

336 lines (288 loc) 8.22 kB
/* eslint-disable no-underscore-dangle */ import { Component, createElement } from 'react'; import isEqual from 'lodash/isEqual'; import omit from 'lodash/omit'; import assign from 'lodash/assign'; import PropTypes from 'prop-types'; import { getValue, getCurrentValue, prefixName } from './utils'; import unknownProps from './unknownProps'; class Field extends Component { static propTypes = { name: PropTypes.string.isRequired, component: PropTypes.oneOfType([PropTypes.func, PropTypes.string]) .isRequired, normalize: PropTypes.func, format: PropTypes.func, validationError: PropTypes.string, validationErrors: PropTypes.object, validateOnBlur: PropTypes.bool, validateOnChange: PropTypes.bool, clearErrorOnFocus: PropTypes.bool }; // validationError为默认错误提示 // validationErrors为指定校验规则所对应的错误提示 static defaultProps = { value: '', validationError: '', validationErrors: {}, validateOnBlur: true, validateOnChange: true, clearErrorOnFocus: true }; static contextTypes = { zentForm: PropTypes.object }; constructor(props, context) { super(props, context); if (!context.zentForm) { throw new Error('Field must be in zent-form'); } this.state = { _value: props.value, _isValid: true, _isDirty: false, _isValidating: false, _initialValue: props.value, _validationError: [], _externalError: null, _asyncValidated: false }; this._name = prefixName(context.zentForm, props.name); this._validations = props.validations || {}; } shouldComponentUpdate(nextProps, nextState) { return !isEqual(nextState, this.state) || !isEqual(nextProps, this.props); } componentWillMount() { if (!this.props.name) { throw new Error('Form Field requires a name property when used'); } const zentForm = this.context.zentForm; zentForm.attachToForm(this); this._name = prefixName(zentForm, this.props.name); if (this.context.zentForm.getSubFieldArray) { const currentValue = this.context.zentForm.getSubFieldArray(this._name); currentValue && this.setState({ _value: currentValue }); } } componentWillReceiveProps(nextProps) { if ('validations' in nextProps) { this._validations = nextProps.validations; } this._name = prefixName(this.context.zentForm, nextProps.name); if (this.context.zentForm.getSubFieldArray) { const currentValue = this.context.zentForm.getSubFieldArray(this._name); currentValue && this.setState({ _value: currentValue }); } } componentDidUpdate(prevProps) { // 支持props中的value动态更新 if (!isEqual(this.props.value, prevProps.value)) { this.setValue(this.props.value); } // 动态改变validation方法,重新校验 // if (!isEqual(this.props.validations, prevProps.validations)) { // this.context.zentForm.validate(this); // } } componentWillUnmount() { this.context.zentForm.detachFromForm(this); } isDirty = () => { return this.state._isDirty; }; isValid = () => { return this.state._isValid; }; isValidating = () => { return this.state._isValidating; }; isActive = () => { return this.state._active; }; getInitialValue = () => { return this.state._initialValue; }; getValue = () => { return this.state._value; }; getName = () => { return this._name; }; isAsyncValidated = () => { return this.state._asyncValidated; }; setValue = (value, needValidate = true) => { this.setState( { _value: value, _isDirty: needValidate }, () => { this.context.zentForm.validate(this); } ); }; resetValue = () => { this.setState( { _value: this.state._initialValue, _isDirty: false }, () => { this.context.zentForm.validate(this); } ); }; setInitialValue = value => { this.setState( { _value: value || this.state._initialValue, _initialValue: value || this.state._initialValue, _isDirty: false }, () => { this.context.zentForm.validate(this); } ); }; getWrappedComponent = () => { return this.wrappedComponent; }; getErrorMessage = () => { const errors = this.getErrorMessages(); return errors.length ? errors[0] : null; }; getErrorMessages = () => { const { _externalError, _validationError } = this.state; return !this.isValid() ? _externalError || _validationError || [] : []; }; normalize = value => { const { normalize } = this.props; if (!normalize) { return value; } const previousValues = this.context.zentForm.getFormValues(); const previousValue = this.getValue(); const nextValues = { ...previousValues, [this.getName()]: value }; return normalize(value, previousValue, nextValues, previousValues); }; format = value => { const { format } = this.props; if (!format) { return value; } return format(value); }; handleChange = (event, options = { merge: false }) => { const { onChange, validateOnChange } = this.props; const previousValue = this.getValue(); const currentValue = options.merge ? getCurrentValue(getValue(event), previousValue) : getValue(event); const newValue = this.normalize(currentValue); let preventSetValue = false; // 在传入的onChange中可以按需阻止更新value值 if (onChange) { onChange(event, newValue, previousValue, () => (preventSetValue = true)); } if (!preventSetValue) { this.setValue(newValue, validateOnChange); this.context.zentForm.onChangeFieldArray && this.context.zentForm.onChangeFieldArray(this._name, newValue); } }; handleFocus = event => { const { onFocus, clearErrorOnFocus } = this.props; let data = { _active: true }; if (onFocus) { onFocus(event); } if (clearErrorOnFocus) { assign(data, { _isValid: true, _validationError: [], _externalError: null }); } this.setState(data); }; handleBlur = (event, options = { merge: false }) => { const { onBlur, asyncValidation, validateOnBlur } = this.props; const previousValue = this.getValue(); const currentValue = options.merge ? getCurrentValue(getValue(event), previousValue) : getValue(event); const newValue = this.normalize(currentValue); let preventSetValue = false; if (onBlur) { onBlur(event, newValue, previousValue, () => (preventSetValue = true)); } this.setState({ _active: false }); if (!preventSetValue) { this.setValue(newValue, validateOnBlur); if (asyncValidation) { this.context.zentForm.asyncValidate(this, newValue); } } }; processProps = props => { const { type, value, ...rest } = props; if (type === 'checkbox') { return { ...rest, checked: !!value, type }; } if (type === 'file') { return { ...rest, type }; } return props; }; render() { const { component, ...rest } = this.props; const passableProps = this.processProps({ ...rest, ref: ref => { this.wrappedComponent = ref; }, name: this.getName(), isTouched: this.isDirty(), isDirty: this.isDirty(), isValid: this.isValid(), isAsyncValidated: this.isAsyncValidated, isActive: this.isActive(), value: this.format(this.getValue()), error: this.getErrorMessage(), errors: this.getErrorMessages(), onChange: this.handleChange, onBlur: this.handleBlur, onFocus: this.handleFocus }); // 原生的标签不能传非标准属性进去 if (typeof component === 'string') { return createElement(component, { ...omit(passableProps, unknownProps) }); } return createElement(component, passableProps); } } export default Field;