easy-react-form
Advanced tools
Comparing version 1.3.4 to 2.0.0-beta.2
<!-- pass through `required` property even when the field is not empty: maybe add some `passThroughRequiredWhenNotEmpty` configuration option. --> | ||
2.0.0-beta.1 / 31.01.2023 | ||
================== | ||
* Refactored the code regarding form state. A new "major" version means that there might hypothetically be some unforeseen accidental bugs. | ||
* Changed the empty value from `undefined` to `null`. | ||
* Input Components now should use `React.forwardRef()` in order to be focusable. | ||
* Bumped React version to `18.2.0`. | ||
* Added properties: `initialState` and `onStateChange(newState)`. | ||
* List Plugin: renamed `<Field i/>` property to `<Field item/>`. | ||
1.2.0 / 09.08.2021 | ||
@@ -4,0 +19,0 @@ ================== |
@@ -7,2 +7,16 @@ "use strict"; | ||
exports.unregisterField = exports.showFieldError = exports.setFormSubmitting = exports.setFieldValue = exports.setFieldValidationError = exports.setFieldError = exports.removeField = exports.registerField = exports.fieldFocused = void 0; | ||
// With the current implementation, actions "mutate" the original `state` object | ||
// instead of creating a new one every time. | ||
// | ||
// One rationale is that this way it might theoretically be more performant | ||
// to reuse and "mutate" the existing `state` object instead of creating a new one | ||
// on each keystroke. | ||
// | ||
// The most significant rationale is that mutating the original `state` object directly | ||
// eliminates any potential "race condition" bugs where state changes would be lost. | ||
// Consider two consequtive action calls: one to set a field's value | ||
// and the other one to focus the field. If the original `state` object is mutated, | ||
// no changes are lost. But if a new state object would've been created by each | ||
// of those two actions, the second one would overwrite the changes made by the first one. | ||
var registerField = function registerField(_ref) { | ||
@@ -26,2 +40,3 @@ var field = _ref.field, | ||
state.fields[field] = 1; | ||
var validationError = validate(value); | ||
@@ -31,7 +46,5 @@ // Only initializes the field with its default `value` | ||
state.values[field] = value; | ||
state.validationErrors[field] = validate(value); | ||
if (error) { | ||
state.errors[field] = error; | ||
state.showErrors[field] = true; | ||
} | ||
state.validationErrors[field] = validationError; | ||
state.errors[field] = error; | ||
state.showErrors[field] = Boolean(error || validationError); | ||
} else { | ||
@@ -45,10 +58,35 @@ state.fields[field]++; | ||
return function (state) { | ||
// Uses a numerical counter instead of a boolean. | ||
// This library uses a numerical counter for tracking a field's "presence" status. | ||
// https://github.com/erikras/redux-form/issues/1705 | ||
// Even if the registration counter for a field | ||
// becomes equal to `0` it's still not destroyed, | ||
// because theoretically it could be a new field | ||
// being added in the beginning of the form | ||
// therefore causing all field to unregister and then register again. | ||
// If those fields were destroyed then their values would be lost. | ||
// | ||
// The reason is that React re-uses existing (mounted) components | ||
// when their properties change, resulting in situtations when | ||
// a field gets inserted or removed from the list of fields | ||
// causing a wave of "unregister"/"register" events. | ||
// | ||
// For example: | ||
// | ||
// <Field name="one"/> | ||
// <Field name="two"/> | ||
// <Field name="three"/> | ||
// | ||
// At some point becomes: | ||
// | ||
// <Field name="one"/> | ||
// <Field name="four"/> | ||
// <Field name="two"/> | ||
// | ||
// In that case, React: | ||
// * Re-purposes `<Field name="two"/>` for rendering `<Field name="four"/>` | ||
// * Re-purposes `<Field name="three"/>` for rendering `<Field name="two"/>` | ||
// | ||
// The first of the two results in "unregiser"-ing `<Field name="two"/>`. | ||
// The second of the two results in re-"regiser"-ing `<Field name="two"/>`. | ||
// | ||
// In order for `<Field name="two"/>` value to not get lost, it has to be retained | ||
// after it gets "unregister"-ed. | ||
// | ||
// So even if the registration counter for a field becomes equal to `0`, | ||
// it's still not destroyed, because it could reappear at some other position in the form. | ||
// | ||
state.fields[field]--; | ||
@@ -71,4 +109,5 @@ }; | ||
return function (state) { | ||
var validationError = state.validationErrors[field]; | ||
state.errors[field] = error; | ||
state.showErrors[field] = Boolean(state.validationErrors[field] || state.errors[field]); | ||
state.showErrors[field] = Boolean(validationError || error); | ||
}; | ||
@@ -79,6 +118,7 @@ }; | ||
exports.setFieldError = setFieldError; | ||
var setFieldValidationError = function setFieldValidationError(field, error) { | ||
var setFieldValidationError = function setFieldValidationError(field, validationError) { | ||
return function (state) { | ||
state.validationErrors[field] = error; | ||
state.showErrors[field] = Boolean(state.validationErrors[field] || state.errors[field]); | ||
var error = state.errors[field]; | ||
state.validationErrors[field] = validationError; | ||
state.showErrors[field] = Boolean(validationError || error); | ||
}; | ||
@@ -85,0 +125,0 @@ }; |
@@ -9,5 +9,3 @@ "use strict"; | ||
var _react = _interopRequireWildcard(require("react")); | ||
var _reactDom = _interopRequireDefault(require("react-dom")); | ||
var _propTypes = _interopRequireDefault(require("prop-types")); | ||
var _reactCreateRef = _interopRequireDefault(require("react-create-ref")); | ||
var _form = require("./form"); | ||
@@ -46,2 +44,3 @@ var _list = require("./list"); | ||
} | ||
var itemType = _propTypes["default"].number; | ||
var FormField = /*#__PURE__*/function (_Component) { | ||
@@ -54,5 +53,6 @@ _inherits(FormField, _Component); | ||
_this = _super.call(this, props); | ||
// The field could register itself inside `componentDidMount` | ||
// but in that case initial `value` wouldn't yet be applied at mount time. | ||
_defineProperty(_assertThisInitialized(_this), "field", (0, _reactCreateRef["default"])()); | ||
_defineProperty(_assertThisInitialized(_this), "field", /*#__PURE__*/_react["default"].createRef()); | ||
_defineProperty(_assertThisInitialized(_this), "onChange", function () { | ||
@@ -94,3 +94,3 @@ for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { | ||
onBlur = _this$props3.onBlur; | ||
var error = _this.validate(context.values[_this.getName()]); | ||
var error = _this.validate(context.state.values[_this.getName()]); | ||
if (error) { | ||
@@ -146,5 +146,5 @@ context.dispatch((0, _actions.setFieldValidationError)(_this.getName(), error)); | ||
if (validate) { | ||
// `context.values` could be replaced with | ||
// `context.state.values` could be replaced with | ||
// something else, like `context.getValues()` | ||
// because `<List/>` values are prefixed in `context.values`. | ||
// because `<List/>` values are prefixed in `context.state.values`. | ||
// But running RegExps and re-creating the object | ||
@@ -167,6 +167,6 @@ // on each `validate()` call seems like a not-the-best architecture. | ||
var listContext = props.listContext, | ||
i = props.i, | ||
item = props.item, | ||
name = props.name; | ||
if (listContext) { | ||
return listContext.getFieldName(i, name); | ||
return listContext.getFieldNameInsideList(item, name); | ||
} | ||
@@ -201,3 +201,3 @@ return name; | ||
if (listContext) { | ||
listContext.onRegisterField(name); | ||
listContext.onRegisterFieldInsideList(name); | ||
} | ||
@@ -263,4 +263,4 @@ } | ||
var context = this.props.context; | ||
var value = context.values[this.getName()]; | ||
var showError = context.showErrors[this.getName()]; | ||
var value = context.state.values[this.getName()]; | ||
var showError = context.state.showErrors[this.getName()]; | ||
@@ -285,21 +285,3 @@ // If the `error` is set then indicate this field as being invalid. | ||
value: function getNode() { | ||
if (this.field.current) { | ||
// Using `ReactDOM.findDOMNode` instead of `this.field.current` | ||
// to supports non-functional components that don't use `React.forwardRef()`. | ||
// For example, `<DropFileUpload/>` from `react-responsive-ui`. | ||
// | ||
// Using `useImperativeHandle()` would throw an error here: | ||
// "Argument appears to not be a ReactComponent. Keys: focus". | ||
// | ||
try { | ||
return _reactDom["default"].findDOMNode(this.field.current); | ||
} catch (error) { | ||
// A workaround for components that use `useImperativeHandle()` | ||
// for adding `ref` suport. | ||
if (this.field.current.getDOMNode) { | ||
return this.field.current.getDOMNode(); | ||
} | ||
console.warn(error); | ||
} | ||
} | ||
return this.field.current; | ||
} | ||
@@ -316,5 +298,5 @@ | ||
component = _this$props7.component; | ||
var value = context.values[this.getName()]; | ||
var error = context.validationErrors[this.getName()] || context.errors[this.getName()]; | ||
var showError = context.showErrors[this.getName()]; | ||
var value = context.state.values[this.getName()]; | ||
var error = context.state.validationErrors[this.getName()] || context.state.errors[this.getName()]; | ||
var showError = context.state.showErrors[this.getName()]; | ||
return /*#__PURE__*/_react["default"].createElement(component, _objectSpread(_objectSpread({}, (0, _utility.getPassThroughProps)(this.props, FormField.propTypes)), {}, { | ||
@@ -325,3 +307,3 @@ ref: this.field, | ||
onBlur: this.onBlur, | ||
disabled: disabled || context.submitting, | ||
disabled: disabled || context.state.submitting, | ||
error: showError ? error : undefined, | ||
@@ -344,3 +326,3 @@ required: required ? true : false, | ||
listContext: _list.listContextPropType, | ||
i: _propTypes["default"].number | ||
item: itemType | ||
}); | ||
@@ -347,0 +329,0 @@ function isValueEmpty(_) { |
@@ -9,3 +9,2 @@ "use strict"; | ||
var _propTypes = _interopRequireDefault(require("prop-types")); | ||
var _createReactContext = _interopRequireDefault(require("create-react-context")); | ||
var _OnAbandonPlugin = _interopRequireDefault(require("./plugins/OnAbandonPlugin")); | ||
@@ -38,4 +37,5 @@ var _ListPlugin = _interopRequireDefault(require("./plugins/ListPlugin")); | ||
function _toPrimitive(input, hint) { if (_typeof(input) !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (_typeof(res) !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } | ||
var Context = (0, _createReactContext["default"])(); | ||
var Context = /*#__PURE__*/_react["default"].createContext(); | ||
exports.Context = Context; | ||
var EMPTY_VALUE = null; | ||
var Form = /*#__PURE__*/function (_Component) { | ||
@@ -66,2 +66,8 @@ _inherits(Form, _Component); | ||
// React doesn't know how to properly handle `value === undefined`. | ||
// https://stackoverflow.com/a/74229877/970769 | ||
if (value === undefined) { | ||
value = EMPTY_VALUE; | ||
} | ||
// The stored field info is used to `validate()` field `value`s | ||
@@ -100,28 +106,20 @@ // and set the corresponding `error`s | ||
_defineProperty(_assertThisInitialized(_this), "dispatch", function (action, callback) { | ||
action(_this.state); | ||
// const newState = action(this.getState()) | ||
// this.applyStateChanges(newState, callback) | ||
// A `React.Component` always re-renders on `this.setState()`, | ||
// even if the `state` hasn't changed. | ||
// The re-rendering of the `<Form/>` is used to re-render | ||
// the `<Field/`>s with the updated `value`s. | ||
// This could potentially result in slower performance | ||
// on `<Form/>`s with a lots of `<Field/>`s | ||
// (maybe hundreds or something like that?) | ||
// but on regular `<Form/>`s I didn't notice any lag. | ||
// A possible performance optimization could be | ||
// not calling `this.setState()` for `<Form/>` re-rendering | ||
// and instead calling something like `this.forceUpdate()` | ||
// on the `<Field/>` that called `context.dispatch()`. | ||
// | ||
// `this.setState()` is called on `this.state` | ||
// rather than creating a new `state` because `this.state` | ||
// is used as the `context` property for `React.Context` | ||
// meaning that `state` reference shouldn't change. | ||
// | ||
_this.updateState(_this.state, callback); | ||
// See the comments in `actions.js` for the rationale | ||
// on why the original `state` gets mutated instead of | ||
// creating a new `state` object. | ||
action(_this.getState()); | ||
_this.applyStateChanges(_this.getState(), callback); | ||
}); | ||
_defineProperty(_assertThisInitialized(_this), "updateState", function (stateUpdater) { | ||
// const newState = stateUpdater(this.state) | ||
// this.applyStateChanges(newState) | ||
// const { onStateChange } = this.props | ||
// if (onStateChange) { | ||
// onStateChange(this.state) | ||
// } | ||
// See the comments in `actions.js` for the rationale | ||
// on why the original `state` gets mutated instead of | ||
// creating a new `state` object. | ||
stateUpdater(_this.getState()); | ||
_this.applyStateChanges(_this.getState()); | ||
}); | ||
@@ -148,3 +146,4 @@ _defineProperty(_assertThisInitialized(_this), "getSubmittedValue", function (value) { | ||
_defineProperty(_assertThisInitialized(_this), "getInitialValue", function (name) { | ||
var initialValues = _this.state.initialValues; | ||
var _this$getState = _this.getState(), | ||
initialValues = _this$getState.initialValues; | ||
for (var _iterator = _createForOfIteratorHelperLoose(_this.plugins), _step; !(_step = _iterator()).done;) { | ||
@@ -162,5 +161,5 @@ var plugin = _step.value; | ||
_defineProperty(_assertThisInitialized(_this), "values", function () { | ||
var _this$state = _this.state, | ||
values = _this$state.values, | ||
fields = _this$state.fields; | ||
var _this$getState2 = _this.getState(), | ||
values = _this$getState2.values, | ||
fields = _this$getState2.fields; | ||
return _this.applyPluginValueTransforms((0, _utility.getValues)(values, fields)); | ||
@@ -177,9 +176,4 @@ }); | ||
var _this$props = _this.props, | ||
autoFocus = _this$props.autoFocus, | ||
plugins = _this$props.plugins, | ||
wait = _this$props.wait; | ||
var _this$state2 = _this.state, | ||
fields = _this$state2.fields, | ||
initialValues = _this$state2.initialValues, | ||
resetCounter = _this$state2.resetCounter; | ||
for (var _iterator2 = _createForOfIteratorHelperLoose(_this.plugins), _step2; !(_step2 = _iterator2()).done;) { | ||
@@ -197,16 +191,6 @@ var plugin = _step2.value; | ||
// Changing `resetCounter` results in a complete re-mounting of the `<form/>`, | ||
// including all of the `<Field/>`s. | ||
_this.state.resetCounter = (0, _utility.getNext)(resetCounter); | ||
// All `<Field/>`s will be re-mounted and re-registered. | ||
var initialFormState = generateInitialFormState(initialValues, { | ||
submitting: wait | ||
}); | ||
for (var _i = 0, _Object$keys = Object.keys(initialFormState); _i < _Object$keys.length; _i++) { | ||
var key = _Object$keys[_i]; | ||
_this.state[key] = initialFormState[key]; | ||
} | ||
var newState = _this.getInitialState(); | ||
// `generateInitialFormState()` produces a state with zero `fields` counters. | ||
// `this.getInitialState()` produces a state with zero `fields` counters. | ||
// But, subsequently, the change to `resetCounter` results in a complete | ||
@@ -219,7 +203,7 @@ // re-mounting of the `<form/>`, including all of the `<Field/>`s, which | ||
// Preserving the current non-zero `fields` counters fixes that. | ||
_this.state.fields = fields; | ||
newState.fields = _this.getState().fields; | ||
// Reset first focusable field since the form is gonna be reset. | ||
// Reset the first focusable field since the form is gonna be reset. | ||
_this.firstField = undefined; | ||
_this.updateState(_this.state, function () { | ||
_this.applyStateChanges(newState, function () { | ||
if (!_this.mounted) { | ||
@@ -229,2 +213,3 @@ return; | ||
// Autofocus the form (if not configured otherwise) | ||
var autoFocus = _this.props.autoFocus; | ||
if (autoFocus) { | ||
@@ -235,3 +220,3 @@ // If `reset()` was called inside `onSubmit()`, then | ||
// are no longer disabled. | ||
if (_this.state.submitting) { | ||
if (_this.getState().submitting) { | ||
_this.focusableBeforeSubmit = _this.getFocusable(); | ||
@@ -243,4 +228,4 @@ } else { | ||
// Trigger each `<Field/>`'s `onChange()` handler. | ||
for (var _i2 = 0, _Object$keys2 = Object.keys(fields); _i2 < _Object$keys2.length; _i2++) { | ||
var _field = _Object$keys2[_i2]; | ||
for (var _i = 0, _Object$keys = Object.keys(_this.getState().fields); _i < _Object$keys.length; _i++) { | ||
var _field = _Object$keys[_i]; | ||
// If the field is still mounted. | ||
@@ -256,2 +241,4 @@ if (_this.fields[_field]) { | ||
} | ||
}, { | ||
resetForm: true | ||
}); | ||
@@ -288,5 +275,6 @@ }); | ||
_defineProperty(_assertThisInitialized(_this), "cleanUpRemovedFields", function () { | ||
var fields = _this.state.fields; | ||
for (var _i3 = 0, _Object$keys3 = Object.keys(fields); _i3 < _Object$keys3.length; _i3++) { | ||
var field = _Object$keys3[_i3]; | ||
var _this$getState3 = _this.getState(), | ||
fields = _this$getState3.fields; | ||
for (var _i2 = 0, _Object$keys2 = Object.keys(fields); _i2 < _Object$keys2.length; _i2++) { | ||
var field = _Object$keys2[_i2]; | ||
// Remove unmounted `<Field/>`s. | ||
@@ -324,3 +312,3 @@ if (fields[field] === 0) { | ||
// (i.e. submit is in progress) | ||
if (_this.state.submitting) { | ||
if (_this.getState().submitting) { | ||
return; | ||
@@ -354,6 +342,6 @@ } | ||
_defineProperty(_assertThisInitialized(_this), "clear", function (field) { | ||
return _this.set(field, undefined); | ||
return _this.set(field, EMPTY_VALUE); | ||
}); | ||
_defineProperty(_assertThisInitialized(_this), "get", function (field) { | ||
return _this.state.values[field]; | ||
return _this.getState().values[field]; | ||
}); | ||
@@ -386,2 +374,5 @@ _defineProperty(_assertThisInitialized(_this), "set", function (field, value) { | ||
}); | ||
_defineProperty(_assertThisInitialized(_this), "getState", function () { | ||
return _this.state.state; | ||
}); | ||
_defineProperty(_assertThisInitialized(_this), "setFormNode", function (node) { | ||
@@ -393,23 +384,3 @@ return _this.form = node; | ||
}); | ||
var _this$props3 = _this.props, | ||
_values = _this$props3.values, | ||
requiredMessage = _this$props3.requiredMessage, | ||
plugins = _this$props3.plugins, | ||
_wait = _this$props3.wait; | ||
_this.state = _objectSpread(_objectSpread({ | ||
resetCounter: 0 | ||
}, generateInitialFormState(_values, { | ||
submitting: _wait | ||
})), {}, { | ||
dispatch: _this.dispatch, | ||
onRegisterField: _this.onRegisterField, | ||
onUnregisterField: _this.onUnregisterField, | ||
getSubmittedValue: _this.getSubmittedValue, | ||
getRequiredMessage: function getRequiredMessage() { | ||
return requiredMessage; | ||
}, | ||
// These're used by `<List/>`. | ||
focus: _this.focus, | ||
getValues: _this.values | ||
}); | ||
var plugins = _this.props.plugins; | ||
_this.plugins = plugins.map(function (Plugin) { | ||
@@ -419,11 +390,6 @@ return new Plugin(function () { | ||
}, function () { | ||
return _this.state; | ||
return _this.getState(); | ||
}); | ||
}); | ||
for (var _iterator5 = _createForOfIteratorHelperLoose(_this.plugins), _step5; !(_step5 = _iterator5()).done;) { | ||
var plugin = _step5.value; | ||
if (plugin.initContext) { | ||
plugin.initContext(_this.state); | ||
} | ||
} | ||
_this.state = _this.getInitialContext(); | ||
return _this; | ||
@@ -444,4 +410,4 @@ } | ||
for (var _iterator6 = _createForOfIteratorHelperLoose(this.plugins), _step6; !(_step6 = _iterator6()).done;) { | ||
var plugin = _step6.value; | ||
for (var _iterator5 = _createForOfIteratorHelperLoose(this.plugins), _step5; !(_step5 = _iterator5()).done;) { | ||
var plugin = _step5.value; | ||
if (plugin.onMount) { | ||
@@ -465,2 +431,11 @@ plugin.onMount(); | ||
this.cleanUpRemovedFields(); | ||
for (var _iterator6 = _createForOfIteratorHelperLoose(this.plugins), _step6; !(_step6 = _iterator6()).done;) { | ||
var plugin = _step6.value; | ||
if (plugin.onUpdate) { | ||
plugin.onUpdate({ | ||
getState: this.getState, | ||
dispatch: this.dispatch | ||
}); | ||
} | ||
} | ||
} | ||
@@ -479,19 +454,130 @@ }, { | ||
}, { | ||
key: "updateState", | ||
value: function updateState(newState, callback) { | ||
key: "getInitialState", | ||
value: function getInitialState() { | ||
var initialState = this._getInitialState(); | ||
for (var _iterator8 = _createForOfIteratorHelperLoose(this.plugins), _step8; !(_step8 = _iterator8()).done;) { | ||
var plugin = _step8.value; | ||
if (plugin.getInitialState) { | ||
initialState = plugin.getInitialState(initialState); | ||
} | ||
} | ||
return initialState; | ||
} | ||
}, { | ||
key: "_getInitialState", | ||
value: function _getInitialState() { | ||
var _this$props3 = this.props, | ||
values = _this$props3.values, | ||
wait = _this$props3.wait; | ||
return generateInitialFormState(this.state ? this.getState().initialValues : values, { | ||
submitting: wait | ||
}); | ||
} | ||
}, { | ||
key: "getInitialContext", | ||
value: function getInitialContext() { | ||
var initialContext = this._getInitialContext(); | ||
// Add `context` functions by plugins. | ||
for (var _iterator9 = _createForOfIteratorHelperLoose(this.plugins), _step9; !(_step9 = _iterator9()).done;) { | ||
var plugin = _step9.value; | ||
if (plugin.getContextFunctions) { | ||
var contextFunctions = plugin.getContextFunctions(); | ||
for (var _i3 = 0, _Object$keys3 = Object.keys(contextFunctions); _i3 < _Object$keys3.length; _i3++) { | ||
var name = _Object$keys3[_i3]; | ||
initialContext[name] = contextFunctions[name]({ | ||
updateState: initialContext.updateState | ||
}); | ||
} | ||
} | ||
} | ||
return initialContext; | ||
} | ||
}, { | ||
key: "_getInitialContext", | ||
value: function _getInitialContext() { | ||
var _this$props4 = this.props, | ||
requiredMessage = _this$props4.requiredMessage, | ||
initialState = _this$props4.initialState; | ||
return { | ||
state: initialState || this.getInitialState(), | ||
// initialState, | ||
resetCounter: 0, | ||
dispatch: this.dispatch, | ||
updateState: this.updateState, | ||
onRegisterField: this.onRegisterField, | ||
onUnregisterField: this.onUnregisterField, | ||
getSubmittedValue: this.getSubmittedValue, | ||
getRequiredMessage: function getRequiredMessage() { | ||
return requiredMessage; | ||
}, | ||
// These're used by `<List/>`. | ||
focus: this.focus, | ||
getValues: this.values | ||
}; | ||
} | ||
}, { | ||
key: "applyStateChanges", | ||
value: function applyStateChanges(newState, callback) { | ||
var _ref3 = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}, | ||
resetForm = _ref3.resetForm; | ||
var prevContext = this.getContext(); | ||
var prevState = prevContext.state; | ||
// Call `onStateChange()` listener. | ||
var onStateChange = this.props.onStateChange; | ||
if (onStateChange) { | ||
onStateChange(newState); | ||
} | ||
// Run any field value watchers: | ||
// | ||
// See if any fields are watched. | ||
// If they are, see if their values have changed. | ||
// If they have, re-render the form after updating state. | ||
for (var _iterator8 = _createForOfIteratorHelperLoose(this.watchedFieldsList), _step8; !(_step8 = _iterator8()).done;) { | ||
var field = _step8.value; | ||
var prevValue = this.state.values[field]; | ||
var newValue = newState.values[field]; | ||
if (newValue !== prevValue) { | ||
// | ||
// This piece of code currently doesn't do anything | ||
// because `prevState` is always equal to `newState`. | ||
// Maybe it could be used in some future if new state objects would be created | ||
// instead of mutating the existing state object. | ||
// | ||
var shouldReRenderForm = false; | ||
for (var _iterator10 = _createForOfIteratorHelperLoose(this.watchedFieldsList), _step10; !(_step10 = _iterator10()).done;) { | ||
var field = _step10.value; | ||
if (newState.values[field] !== prevState.values[field]) { | ||
// Re-render the form after updating state. | ||
newState = _objectSpread({}, newState); | ||
shouldReRenderForm = true; | ||
break; | ||
} | ||
} | ||
// Update state. | ||
this.setState(newState, callback); | ||
// | ||
// A `React.Component` always re-renders on `this.setState()`, | ||
// even if the `state` hasn't changed. | ||
// The re-rendering of the `<Form/>` is used to re-render | ||
// the `<Field/`>s with the updated `value`s. | ||
// This could potentially result in slower performance | ||
// on `<Form/>`s with a lots of `<Field/>`s | ||
// (maybe hundreds or something like that?) | ||
// but on regular `<Form/>`s I didn't notice any lag. | ||
// A possible performance optimization could be | ||
// not calling `this.setState()` for `<Form/>` re-rendering | ||
// and instead calling something like `this.forceUpdate()` | ||
// on just the exact `<Field/>` that called `context.dispatch(action)`. | ||
// | ||
// `this.setState()` is called on `this.state` rather than creating | ||
// a new `state`, because `this.state` is used as the `context` property | ||
// for `React.Context`, so if `state` reference changes, the whole form | ||
// gets re-rendered, and that's not something that should be done | ||
// unless any "field value watchers" have been triggered due to a changed field value. | ||
// | ||
var newContext = shouldReRenderForm ? _objectSpread({}, prevContext) : prevContext; | ||
newContext.state = newState; | ||
if (resetForm) { | ||
// Changing the `resetCounter` property results in a complete re-mounting | ||
// of the `<form/>`, including all of the `<Field/>`s. | ||
newContext.resetCounter = (0, _utility.getNext)(newContext.resetCounter); | ||
} | ||
this.setState(newContext, callback); | ||
} | ||
@@ -510,4 +596,4 @@ | ||
function applyPluginValueTransforms(values) { | ||
for (var _iterator9 = _createForOfIteratorHelperLoose(this.plugins), _step9; !(_step9 = _iterator9()).done;) { | ||
var plugin = _step9.value; | ||
for (var _iterator11 = _createForOfIteratorHelperLoose(this.plugins), _step11; !(_step11 = _iterator11()).done;) { | ||
var plugin = _step11.value; | ||
if (plugin.getValues) { | ||
@@ -524,6 +610,6 @@ values = plugin.getValues(values); | ||
value: function searchForInvalidField() { | ||
var _this$state3 = this.state, | ||
fields = _this$state3.fields, | ||
values = _this$state3.values, | ||
errors = _this$state3.errors; | ||
var _this$getState4 = this.getState(), | ||
fields = _this$getState4.fields, | ||
values = _this$getState4.values, | ||
errors = _this$getState4.errors; | ||
@@ -559,5 +645,5 @@ // Re-run `validate()` for each field. | ||
var scrollDuration = this.props.scrollDuration; | ||
var _this$state4 = this.state, | ||
fields = _this$state4.fields, | ||
values = _this$state4.values; | ||
var _this$getState5 = this.getState(), | ||
fields = _this$getState5.fields, | ||
values = _this$getState5.values; | ||
@@ -614,5 +700,5 @@ // Are there any invalid fields. | ||
value: function getSubmittedValues() { | ||
var _this$state5 = this.state, | ||
fields = _this$state5.fields, | ||
values = _this$state5.values; | ||
var _this$getState6 = this.getState(), | ||
fields = _this$getState6.fields, | ||
values = _this$getState6.values; | ||
// Get only "registered" (non-removed) field values. | ||
@@ -746,8 +832,13 @@ var fieldValues = (0, _utility.getValues)(values, fields); | ||
}, { | ||
key: "getContext", | ||
value: function getContext() { | ||
return this.state; | ||
} | ||
}, { | ||
key: "render", | ||
value: function render() { | ||
var children = this.props.children; | ||
var _this$state6 = this.state, | ||
resetCounter = _this$state6.resetCounter, | ||
submitting = _this$state6.submitting; | ||
var resetCounter = this.getContext.resetCounter; | ||
var _this$getState7 = this.getState(), | ||
submitting = _this$getState7.submitting; | ||
return /*#__PURE__*/_react["default"].createElement("form", _extends({ | ||
@@ -759,3 +850,3 @@ key: resetCounter, | ||
}), /*#__PURE__*/_react["default"].createElement(Context.Provider, { | ||
value: this.state | ||
value: this.getContext() | ||
}, typeof children === 'function' ? /*#__PURE__*/_react["default"].createElement(Children, { | ||
@@ -781,3 +872,5 @@ values: this.mounted ? this.values() : undefined, | ||
onAfterSubmit: _propTypes["default"].func, | ||
onStateChange: _propTypes["default"].func, | ||
onAbandon: _propTypes["default"].func, | ||
initialState: _propTypes["default"].object, | ||
values: _propTypes["default"].object, | ||
@@ -804,12 +897,12 @@ autoFocus: _propTypes["default"].bool.isRequired, | ||
}); | ||
function Children(_ref3) { | ||
var values = _ref3.values, | ||
reset = _ref3.reset, | ||
set = _ref3.set, | ||
clear = _ref3.clear, | ||
scroll = _ref3.scroll, | ||
focus = _ref3.focus, | ||
watch = _ref3.watch, | ||
submitting = _ref3.submitting, | ||
children = _ref3.children; | ||
function Children(_ref4) { | ||
var values = _ref4.values, | ||
reset = _ref4.reset, | ||
set = _ref4.set, | ||
clear = _ref4.clear, | ||
scroll = _ref4.scroll, | ||
focus = _ref4.focus, | ||
watch = _ref4.watch, | ||
submitting = _ref4.submitting, | ||
children = _ref4.children; | ||
return children({ | ||
@@ -839,5 +932,5 @@ values: values, | ||
var initialValues = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
var _ref4 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, | ||
_ref4$submitting = _ref4.submitting, | ||
submitting = _ref4$submitting === void 0 ? false : _ref4$submitting; | ||
var _ref5 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, | ||
_ref5$submitting = _ref5.submitting, | ||
submitting = _ref5$submitting === void 0 ? false : _ref5$submitting; | ||
return { | ||
@@ -865,12 +958,15 @@ // `mounted`/`unmounted` counters for each form field. | ||
var contextPropType = _propTypes["default"].shape({ | ||
fields: _propTypes["default"].object.isRequired, | ||
values: _propTypes["default"].object.isRequired, | ||
initialValues: _propTypes["default"].object.isRequired, | ||
errors: _propTypes["default"].object.isRequired, | ||
validationErrors: _propTypes["default"].object.isRequired, | ||
showErrors: _propTypes["default"].object.isRequired, | ||
submitting: _propTypes["default"].bool.isRequired, | ||
state: _propTypes["default"].shape({ | ||
fields: _propTypes["default"].object.isRequired, | ||
values: _propTypes["default"].object.isRequired, | ||
initialValues: _propTypes["default"].object.isRequired, | ||
errors: _propTypes["default"].object.isRequired, | ||
validationErrors: _propTypes["default"].object.isRequired, | ||
showErrors: _propTypes["default"].object.isRequired, | ||
latestFocusedField: _propTypes["default"].string, | ||
submitting: _propTypes["default"].bool.isRequired | ||
}).isRequired, | ||
updateState: _propTypes["default"].func.isRequired, | ||
onRegisterField: _propTypes["default"].func.isRequired, | ||
onUnregisterField: _propTypes["default"].func.isRequired, | ||
onRegisterList: _propTypes["default"].func.isRequired, | ||
getSubmittedValue: _propTypes["default"].func.isRequired, | ||
@@ -880,5 +976,8 @@ focus: _propTypes["default"].func.isRequired, | ||
getRequiredMessage: _propTypes["default"].func.isRequired, | ||
getValues: _propTypes["default"].func.isRequired | ||
getValues: _propTypes["default"].func.isRequired, | ||
// These get added by `ListPlugin`. | ||
onRegisterList: _propTypes["default"].func.isRequired, | ||
onListStateChange: _propTypes["default"].func.isRequired | ||
}); | ||
exports.contextPropType = contextPropType; | ||
//# sourceMappingURL=form.js.map |
@@ -12,8 +12,5 @@ "use strict"; | ||
var _propTypes = _interopRequireDefault(require("prop-types")); | ||
var _createReactContext = _interopRequireDefault(require("create-react-context")); | ||
var _form = require("./form"); | ||
var _ListPlugin = require("./plugins/ListPlugin.utility"); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } | ||
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } | ||
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
@@ -47,10 +44,10 @@ function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } } | ||
_this = _super.call(this, props); | ||
_defineProperty(_assertThisInitialized(_this), "onReset", function () { | ||
_this.setState(_this.getInitialItemsState()); | ||
_defineProperty(_assertThisInitialized(_this), "reset", function () { | ||
_this.setListState(_this.getInitialListState()); | ||
}); | ||
_defineProperty(_assertThisInitialized(_this), "getFieldName", function (i, name) { | ||
if (typeof i !== 'number') { | ||
throw new Error('Each `<Feild/>` in a `<List/>` must have an `i` property'); | ||
_defineProperty(_assertThisInitialized(_this), "getFieldName", function (itemId, name) { | ||
if (typeof itemId !== 'number') { | ||
throw new Error('Each `<Feild/>` in a `<List/>` must have an `item` property'); | ||
} | ||
return (0, _ListPlugin.getFieldName)(_this.props.name, i, name); | ||
return (0, _ListPlugin.getFieldName)(_this.props.name, itemId, name); | ||
}); | ||
@@ -64,20 +61,23 @@ _defineProperty(_assertThisInitialized(_this), "onRegisterField", function (name) { | ||
var context = _this.props.context; | ||
var _this$state = _this.state, | ||
items = _this$state.items, | ||
itemsCounter = _this$state.itemsCounter; | ||
_this.setState({ | ||
itemsCounter: itemsCounter + 1, | ||
items: items.concat([itemsCounter]) | ||
var _this$getListState = _this.getListState(), | ||
items = _this$getListState.items, | ||
maxItemId = _this$getListState.maxItemId; | ||
var itemId = maxItemId + 1; | ||
_this.setListState({ | ||
maxItemId: itemId, | ||
items: items.concat([itemId]) | ||
}, function () { | ||
var name = _this.props.name; | ||
if (_this.firstFieldName) { | ||
context.focus("".concat(name, ":").concat(itemsCounter, ":").concat(_this.firstFieldName)); | ||
context.focus(_this.getFieldName(itemId, _this.firstFieldName)); | ||
} | ||
}); | ||
}); | ||
_defineProperty(_assertThisInitialized(_this), "remove", function (i) { | ||
var items = _this.state.items; | ||
_this.setState({ | ||
_defineProperty(_assertThisInitialized(_this), "remove", function (itemId) { | ||
var _this$getListState2 = _this.getListState(), | ||
items = _this$getListState2.items, | ||
maxItemId = _this$getListState2.maxItemId; | ||
_this.setListState({ | ||
maxItemId: maxItemId, | ||
items: items.filter(function (_) { | ||
return _ !== i; | ||
return _ !== itemId; | ||
}) | ||
@@ -87,13 +87,15 @@ }); | ||
_defineProperty(_assertThisInitialized(_this), "map", function (func) { | ||
var items = _this.state.items; | ||
return items.map(function (i) { | ||
return func(i); | ||
var _this$getListState3 = _this.getListState(), | ||
items = _this$getListState3.items; | ||
return items.map(function (item) { | ||
return func(item); | ||
}); | ||
}); | ||
_this.state = _objectSpread({ | ||
context: { | ||
getFieldName: _this.getFieldName, | ||
onRegisterField: _this.onRegisterField | ||
} | ||
}, _this.getInitialItemsState()); | ||
_this.state = { | ||
listContext: { | ||
getFieldNameInsideList: _this.getFieldName, | ||
onRegisterFieldInsideList: _this.onRegisterField | ||
}, | ||
state: _this.getInitialListState() | ||
}; | ||
return _this; | ||
@@ -108,27 +110,70 @@ } | ||
context.onRegisterList(name, { | ||
onReset: this.onReset | ||
onReset: this.reset, | ||
state: this.getListState() | ||
}); | ||
} | ||
}, { | ||
key: "componentWillUnmount", | ||
value: function componentWillUnmount() { | ||
var _this$props2 = this.props, | ||
name = _this$props2.name, | ||
context = _this$props2.context; | ||
context.onUnregisterList(name); | ||
} | ||
}, { | ||
key: "getInitialItems", | ||
value: function getInitialItems() { | ||
var _this$props2 = this.props, | ||
context = _this$props2.context, | ||
name = _this$props2.name, | ||
count = _this$props2.count; | ||
if (context.initialValues[name]) { | ||
return createIndexArray(context.initialValues[name].length); | ||
var _this$props3 = this.props, | ||
context = _this$props3.context, | ||
name = _this$props3.name, | ||
count = _this$props3.count; | ||
// console.error(`[easy-react-form] \`initialState\` doesn't include the state for list "${name}"`) | ||
var initialListValue = context.state.initialValues[name]; | ||
if (initialListValue) { | ||
return createItemIdsArray(initialListValue.length); | ||
} | ||
return createIndexArray(count); | ||
return createItemIdsArray(count); | ||
} | ||
}, { | ||
key: "getInitialItemsState", | ||
value: function getInitialItemsState() { | ||
key: "getInitialListState", | ||
value: function getInitialListState() { | ||
// Get list initial state from the form's `initialState`. | ||
var _this$props4 = this.props, | ||
context = _this$props4.context, | ||
name = _this$props4.name; | ||
if (context.state.lists[name]) { | ||
if (context.state.listInstanceCounters[name] > 0) { | ||
return context.state.lists[name]; | ||
} | ||
} | ||
// Create list initial state. | ||
var items = this.getInitialItems(); | ||
return { | ||
items: items, | ||
itemsCounter: items.length | ||
maxItemId: items[items.length - 1] | ||
}; | ||
} | ||
}, { | ||
key: "getListState", | ||
value: function getListState() { | ||
return this.state.state; | ||
} | ||
}, { | ||
key: "setListState", | ||
value: function setListState(state, callback) { | ||
var _this$props5 = this.props, | ||
context = _this$props5.context, | ||
name = _this$props5.name; | ||
context.onListStateChange(name, state); | ||
this.setState({ | ||
state: state | ||
}, callback); | ||
} | ||
}, { | ||
key: "getListContext", | ||
value: function getListContext() { | ||
return this.state.listContext; | ||
} | ||
}, { | ||
key: "render", | ||
@@ -138,6 +183,5 @@ value: function render() { | ||
var children = this.props.children; | ||
var listContext = this.state.context; | ||
return /*#__PURE__*/_react["default"].createElement(_form.Context.Consumer, null, function (context) { | ||
return /*#__PURE__*/_react["default"].createElement(ListContext.Provider, { | ||
value: listContext | ||
value: _this2.getListContext() | ||
}, children({ | ||
@@ -162,5 +206,5 @@ map: _this2.map, | ||
}; | ||
var ListContext = (0, _createReactContext["default"])(); | ||
var ListContext = /*#__PURE__*/_react["default"].createContext(); | ||
exports.ListContext = ListContext; | ||
function createIndexArray(size) { | ||
function createItemIdsArray(size) { | ||
var array = new Array(size); | ||
@@ -175,6 +219,6 @@ var i = 0; | ||
var listContextPropType = _propTypes["default"].shape({ | ||
getFieldName: _propTypes["default"].func.isRequired, | ||
onRegisterField: _propTypes["default"].func.isRequired | ||
getFieldNameInsideList: _propTypes["default"].func.isRequired, | ||
onRegisterFieldInsideList: _propTypes["default"].func.isRequired | ||
}); | ||
exports.listContextPropType = listContextPropType; | ||
//# sourceMappingURL=list.js.map |
@@ -9,2 +9,4 @@ "use strict"; | ||
function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); } | ||
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } | ||
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
@@ -22,13 +24,61 @@ function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } } | ||
_createClass(ListPlugin, [{ | ||
key: "initContext", | ||
value: function initContext(context) { | ||
key: "getContextFunctions", | ||
value: function getContextFunctions() { | ||
var _this = this; | ||
context.onRegisterList = function (name, _ref) { | ||
var onReset = _ref.onReset; | ||
_this.lists[name] = { | ||
onReset: onReset | ||
}; | ||
return { | ||
onRegisterList: function onRegisterList(_ref) { | ||
var updateState = _ref.updateState; | ||
return function (name, _ref2) { | ||
var onReset = _ref2.onReset, | ||
listState = _ref2.state; | ||
_this.lists[name] = { | ||
onReset: onReset | ||
}; | ||
updateState(function (state) { | ||
if (state.listInstanceCounters[name] === undefined) { | ||
state.listInstanceCounters[name] = 1; | ||
} else { | ||
state.listInstanceCounters[name]++; | ||
} | ||
state.lists[name] = listState; | ||
}); | ||
}; | ||
}, | ||
onUnregisterList: function onUnregisterList(_ref3) { | ||
var updateState = _ref3.updateState; | ||
return function (name) { | ||
_this.lists[name] = undefined; | ||
// Doesn't reset the list's `state` in form state | ||
// because the list might get re-mounted at some other position | ||
// in the document during the ongoing React re-render. | ||
updateState(function (state) { | ||
// Doesn't create a new `state` object. | ||
// Instead it "mutates" the original one. | ||
// The rationale is provided in `actions.js`. | ||
state.listInstanceCounters[name]--; | ||
}); | ||
}; | ||
}, | ||
onListStateChange: function onListStateChange(_ref4) { | ||
var updateState = _ref4.updateState; | ||
return function (name, newState) { | ||
updateState(function (state) { | ||
// Doesn't create a new `state` object. | ||
// Instead it "mutates" the original one. | ||
// The rationale is provided in `actions.js`. | ||
state.lists[name] = newState; | ||
}); | ||
}; | ||
} | ||
}; | ||
} | ||
}, { | ||
key: "getInitialState", | ||
value: function getInitialState(state) { | ||
return _objectSpread(_objectSpread({}, state), {}, { | ||
lists: {}, | ||
listInstanceCounters: {} | ||
}); | ||
} | ||
}, { | ||
key: "getValues", | ||
@@ -49,3 +99,4 @@ value: function getValues(values) { | ||
if (this.lists[name]) { | ||
var fields = form.state.fields; | ||
var _form$getState = form.getState(), | ||
fields = _form$getState.fields; | ||
for (var _i = 0, _Object$keys = Object.keys(fields); _i < _Object$keys.length; _i++) { | ||
@@ -61,2 +112,17 @@ var field = _Object$keys[_i]; | ||
} | ||
}, { | ||
key: "onUpdate", | ||
value: function onUpdate(_ref5) { | ||
var getState = _ref5.getState, | ||
dispatch = _ref5.dispatch; | ||
var _getState = getState(), | ||
listInstanceCounters = _getState.listInstanceCounters; | ||
for (var _i2 = 0, _Object$keys2 = Object.keys(listInstanceCounters); _i2 < _Object$keys2.length; _i2++) { | ||
var name = _Object$keys2[_i2]; | ||
// Remove unmounted `<List/>`s. | ||
if (listInstanceCounters[name] === 0) { | ||
dispatch((0, _ListPlugin.removeListAction)(name)); | ||
} | ||
} | ||
} | ||
}]); | ||
@@ -63,0 +129,0 @@ return ListPlugin; |
@@ -9,2 +9,3 @@ "use strict"; | ||
exports.getListValue = getListValue; | ||
exports.removeListAction = void 0; | ||
var _utility = require("../utility"); | ||
@@ -104,2 +105,9 @@ function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); } | ||
} | ||
var removeListAction = function removeListAction(name) { | ||
return function (state) { | ||
delete state.lists[name]; | ||
delete state.listInstanceCounters[name]; | ||
}; | ||
}; | ||
exports.removeListAction = removeListAction; | ||
//# sourceMappingURL=ListPlugin.utility.js.map |
@@ -21,3 +21,3 @@ "use strict"; | ||
return /*#__PURE__*/_react["default"].createElement(props.component, _objectSpread(_objectSpread({}, (0, _utility.getPassThroughProps)(props, Submit.propTypes)), {}, { | ||
wait: context.submitting | ||
wait: context.state.submitting | ||
})); | ||
@@ -24,0 +24,0 @@ }); |
@@ -0,1 +1,15 @@ | ||
// With the current implementation, actions "mutate" the original `state` object | ||
// instead of creating a new one every time. | ||
// | ||
// One rationale is that this way it might theoretically be more performant | ||
// to reuse and "mutate" the existing `state` object instead of creating a new one | ||
// on each keystroke. | ||
// | ||
// The most significant rationale is that mutating the original `state` object directly | ||
// eliminates any potential "race condition" bugs where state changes would be lost. | ||
// Consider two consequtive action calls: one to set a field's value | ||
// and the other one to focus the field. If the original `state` object is mutated, | ||
// no changes are lost. But if a new state object would've been created by each | ||
// of those two actions, the second one would overwrite the changes made by the first one. | ||
export var registerField = function registerField(_ref) { | ||
@@ -19,2 +33,3 @@ var field = _ref.field, | ||
state.fields[field] = 1; | ||
var validationError = validate(value); | ||
@@ -24,7 +39,5 @@ // Only initializes the field with its default `value` | ||
state.values[field] = value; | ||
state.validationErrors[field] = validate(value); | ||
if (error) { | ||
state.errors[field] = error; | ||
state.showErrors[field] = true; | ||
} | ||
state.validationErrors[field] = validationError; | ||
state.errors[field] = error; | ||
state.showErrors[field] = Boolean(error || validationError); | ||
} else { | ||
@@ -37,10 +50,35 @@ state.fields[field]++; | ||
return function (state) { | ||
// Uses a numerical counter instead of a boolean. | ||
// This library uses a numerical counter for tracking a field's "presence" status. | ||
// https://github.com/erikras/redux-form/issues/1705 | ||
// Even if the registration counter for a field | ||
// becomes equal to `0` it's still not destroyed, | ||
// because theoretically it could be a new field | ||
// being added in the beginning of the form | ||
// therefore causing all field to unregister and then register again. | ||
// If those fields were destroyed then their values would be lost. | ||
// | ||
// The reason is that React re-uses existing (mounted) components | ||
// when their properties change, resulting in situtations when | ||
// a field gets inserted or removed from the list of fields | ||
// causing a wave of "unregister"/"register" events. | ||
// | ||
// For example: | ||
// | ||
// <Field name="one"/> | ||
// <Field name="two"/> | ||
// <Field name="three"/> | ||
// | ||
// At some point becomes: | ||
// | ||
// <Field name="one"/> | ||
// <Field name="four"/> | ||
// <Field name="two"/> | ||
// | ||
// In that case, React: | ||
// * Re-purposes `<Field name="two"/>` for rendering `<Field name="four"/>` | ||
// * Re-purposes `<Field name="three"/>` for rendering `<Field name="two"/>` | ||
// | ||
// The first of the two results in "unregiser"-ing `<Field name="two"/>`. | ||
// The second of the two results in re-"regiser"-ing `<Field name="two"/>`. | ||
// | ||
// In order for `<Field name="two"/>` value to not get lost, it has to be retained | ||
// after it gets "unregister"-ed. | ||
// | ||
// So even if the registration counter for a field becomes equal to `0`, | ||
// it's still not destroyed, because it could reappear at some other position in the form. | ||
// | ||
state.fields[field]--; | ||
@@ -61,4 +99,5 @@ }; | ||
return function (state) { | ||
var validationError = state.validationErrors[field]; | ||
state.errors[field] = error; | ||
state.showErrors[field] = Boolean(state.validationErrors[field] || state.errors[field]); | ||
state.showErrors[field] = Boolean(validationError || error); | ||
}; | ||
@@ -68,6 +107,7 @@ }; | ||
// Sets field validation `error`. | ||
export var setFieldValidationError = function setFieldValidationError(field, error) { | ||
export var setFieldValidationError = function setFieldValidationError(field, validationError) { | ||
return function (state) { | ||
state.validationErrors[field] = error; | ||
state.showErrors[field] = Boolean(state.validationErrors[field] || state.errors[field]); | ||
var error = state.errors[field]; | ||
state.validationErrors[field] = validationError; | ||
state.showErrors[field] = Boolean(validationError || error); | ||
}; | ||
@@ -74,0 +114,0 @@ }; |
@@ -19,5 +19,3 @@ function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); } | ||
import React, { Component } from 'react'; | ||
import ReactDOM from 'react-dom'; | ||
import PropTypes from 'prop-types'; | ||
import createRef from 'react-create-ref'; | ||
import { Context, contextPropType } from './form'; | ||
@@ -39,2 +37,3 @@ import { ListContext, listContextPropType } from './list'; | ||
} | ||
var itemType = PropTypes.number; | ||
var FormField = /*#__PURE__*/function (_Component) { | ||
@@ -47,5 +46,6 @@ _inherits(FormField, _Component); | ||
_this = _super.call(this, props); | ||
// The field could register itself inside `componentDidMount` | ||
// but in that case initial `value` wouldn't yet be applied at mount time. | ||
_defineProperty(_assertThisInitialized(_this), "field", createRef()); | ||
_defineProperty(_assertThisInitialized(_this), "field", /*#__PURE__*/React.createRef()); | ||
_defineProperty(_assertThisInitialized(_this), "onChange", function () { | ||
@@ -87,3 +87,3 @@ for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { | ||
onBlur = _this$props3.onBlur; | ||
var error = _this.validate(context.values[_this.getName()]); | ||
var error = _this.validate(context.state.values[_this.getName()]); | ||
if (error) { | ||
@@ -139,5 +139,5 @@ context.dispatch(setFieldValidationError(_this.getName(), error)); | ||
if (validate) { | ||
// `context.values` could be replaced with | ||
// `context.state.values` could be replaced with | ||
// something else, like `context.getValues()` | ||
// because `<List/>` values are prefixed in `context.values`. | ||
// because `<List/>` values are prefixed in `context.state.values`. | ||
// But running RegExps and re-creating the object | ||
@@ -160,6 +160,6 @@ // on each `validate()` call seems like a not-the-best architecture. | ||
var listContext = props.listContext, | ||
i = props.i, | ||
item = props.item, | ||
name = props.name; | ||
if (listContext) { | ||
return listContext.getFieldName(i, name); | ||
return listContext.getFieldNameInsideList(item, name); | ||
} | ||
@@ -194,3 +194,3 @@ return name; | ||
if (listContext) { | ||
listContext.onRegisterField(name); | ||
listContext.onRegisterFieldInsideList(name); | ||
} | ||
@@ -256,4 +256,4 @@ } | ||
var context = this.props.context; | ||
var value = context.values[this.getName()]; | ||
var showError = context.showErrors[this.getName()]; | ||
var value = context.state.values[this.getName()]; | ||
var showError = context.state.showErrors[this.getName()]; | ||
@@ -278,21 +278,3 @@ // If the `error` is set then indicate this field as being invalid. | ||
value: function getNode() { | ||
if (this.field.current) { | ||
// Using `ReactDOM.findDOMNode` instead of `this.field.current` | ||
// to supports non-functional components that don't use `React.forwardRef()`. | ||
// For example, `<DropFileUpload/>` from `react-responsive-ui`. | ||
// | ||
// Using `useImperativeHandle()` would throw an error here: | ||
// "Argument appears to not be a ReactComponent. Keys: focus". | ||
// | ||
try { | ||
return ReactDOM.findDOMNode(this.field.current); | ||
} catch (error) { | ||
// A workaround for components that use `useImperativeHandle()` | ||
// for adding `ref` suport. | ||
if (this.field.current.getDOMNode) { | ||
return this.field.current.getDOMNode(); | ||
} | ||
console.warn(error); | ||
} | ||
} | ||
return this.field.current; | ||
} | ||
@@ -309,5 +291,5 @@ | ||
component = _this$props7.component; | ||
var value = context.values[this.getName()]; | ||
var error = context.validationErrors[this.getName()] || context.errors[this.getName()]; | ||
var showError = context.showErrors[this.getName()]; | ||
var value = context.state.values[this.getName()]; | ||
var error = context.state.validationErrors[this.getName()] || context.state.errors[this.getName()]; | ||
var showError = context.state.showErrors[this.getName()]; | ||
return /*#__PURE__*/React.createElement(component, _objectSpread(_objectSpread({}, getPassThroughProps(this.props, FormField.propTypes)), {}, { | ||
@@ -318,3 +300,3 @@ ref: this.field, | ||
onBlur: this.onBlur, | ||
disabled: disabled || context.submitting, | ||
disabled: disabled || context.state.submitting, | ||
error: showError ? error : undefined, | ||
@@ -337,3 +319,3 @@ required: required ? true : false, | ||
listContext: listContextPropType, | ||
i: PropTypes.number | ||
item: itemType | ||
}); | ||
@@ -340,0 +322,0 @@ function isValueEmpty(_) { |
@@ -23,3 +23,2 @@ function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); } | ||
import PropTypes from 'prop-types'; | ||
import createContext from 'create-react-context'; | ||
import OnAbandonPlugin from './plugins/OnAbandonPlugin'; | ||
@@ -29,3 +28,4 @@ import ListPlugin from './plugins/ListPlugin'; | ||
import { setFormSubmitting as _setFormSubmitting, setFieldValue, setFieldValidationError, registerField, unregisterField, removeField } from './actions'; | ||
export var Context = createContext(); | ||
export var Context = /*#__PURE__*/React.createContext(); | ||
var EMPTY_VALUE = null; | ||
var Form = /*#__PURE__*/function (_Component) { | ||
@@ -56,2 +56,8 @@ _inherits(Form, _Component); | ||
// React doesn't know how to properly handle `value === undefined`. | ||
// https://stackoverflow.com/a/74229877/970769 | ||
if (value === undefined) { | ||
value = EMPTY_VALUE; | ||
} | ||
// The stored field info is used to `validate()` field `value`s | ||
@@ -90,28 +96,20 @@ // and set the corresponding `error`s | ||
_defineProperty(_assertThisInitialized(_this), "dispatch", function (action, callback) { | ||
action(_this.state); | ||
// const newState = action(this.getState()) | ||
// this.applyStateChanges(newState, callback) | ||
// A `React.Component` always re-renders on `this.setState()`, | ||
// even if the `state` hasn't changed. | ||
// The re-rendering of the `<Form/>` is used to re-render | ||
// the `<Field/`>s with the updated `value`s. | ||
// This could potentially result in slower performance | ||
// on `<Form/>`s with a lots of `<Field/>`s | ||
// (maybe hundreds or something like that?) | ||
// but on regular `<Form/>`s I didn't notice any lag. | ||
// A possible performance optimization could be | ||
// not calling `this.setState()` for `<Form/>` re-rendering | ||
// and instead calling something like `this.forceUpdate()` | ||
// on the `<Field/>` that called `context.dispatch()`. | ||
// | ||
// `this.setState()` is called on `this.state` | ||
// rather than creating a new `state` because `this.state` | ||
// is used as the `context` property for `React.Context` | ||
// meaning that `state` reference shouldn't change. | ||
// | ||
_this.updateState(_this.state, callback); | ||
// See the comments in `actions.js` for the rationale | ||
// on why the original `state` gets mutated instead of | ||
// creating a new `state` object. | ||
action(_this.getState()); | ||
_this.applyStateChanges(_this.getState(), callback); | ||
}); | ||
_defineProperty(_assertThisInitialized(_this), "updateState", function (stateUpdater) { | ||
// const newState = stateUpdater(this.state) | ||
// this.applyStateChanges(newState) | ||
// const { onStateChange } = this.props | ||
// if (onStateChange) { | ||
// onStateChange(this.state) | ||
// } | ||
// See the comments in `actions.js` for the rationale | ||
// on why the original `state` gets mutated instead of | ||
// creating a new `state` object. | ||
stateUpdater(_this.getState()); | ||
_this.applyStateChanges(_this.getState()); | ||
}); | ||
@@ -138,3 +136,4 @@ _defineProperty(_assertThisInitialized(_this), "getSubmittedValue", function (value) { | ||
_defineProperty(_assertThisInitialized(_this), "getInitialValue", function (name) { | ||
var initialValues = _this.state.initialValues; | ||
var _this$getState = _this.getState(), | ||
initialValues = _this$getState.initialValues; | ||
for (var _iterator = _createForOfIteratorHelperLoose(_this.plugins), _step; !(_step = _iterator()).done;) { | ||
@@ -152,5 +151,5 @@ var plugin = _step.value; | ||
_defineProperty(_assertThisInitialized(_this), "values", function () { | ||
var _this$state = _this.state, | ||
values = _this$state.values, | ||
fields = _this$state.fields; | ||
var _this$getState2 = _this.getState(), | ||
values = _this$getState2.values, | ||
fields = _this$getState2.fields; | ||
return _this.applyPluginValueTransforms(getValues(values, fields)); | ||
@@ -167,9 +166,4 @@ }); | ||
var _this$props = _this.props, | ||
autoFocus = _this$props.autoFocus, | ||
plugins = _this$props.plugins, | ||
wait = _this$props.wait; | ||
var _this$state2 = _this.state, | ||
fields = _this$state2.fields, | ||
initialValues = _this$state2.initialValues, | ||
resetCounter = _this$state2.resetCounter; | ||
for (var _iterator2 = _createForOfIteratorHelperLoose(_this.plugins), _step2; !(_step2 = _iterator2()).done;) { | ||
@@ -187,16 +181,6 @@ var plugin = _step2.value; | ||
// Changing `resetCounter` results in a complete re-mounting of the `<form/>`, | ||
// including all of the `<Field/>`s. | ||
_this.state.resetCounter = getNext(resetCounter); | ||
// All `<Field/>`s will be re-mounted and re-registered. | ||
var initialFormState = generateInitialFormState(initialValues, { | ||
submitting: wait | ||
}); | ||
for (var _i = 0, _Object$keys = Object.keys(initialFormState); _i < _Object$keys.length; _i++) { | ||
var key = _Object$keys[_i]; | ||
_this.state[key] = initialFormState[key]; | ||
} | ||
var newState = _this.getInitialState(); | ||
// `generateInitialFormState()` produces a state with zero `fields` counters. | ||
// `this.getInitialState()` produces a state with zero `fields` counters. | ||
// But, subsequently, the change to `resetCounter` results in a complete | ||
@@ -209,7 +193,7 @@ // re-mounting of the `<form/>`, including all of the `<Field/>`s, which | ||
// Preserving the current non-zero `fields` counters fixes that. | ||
_this.state.fields = fields; | ||
newState.fields = _this.getState().fields; | ||
// Reset first focusable field since the form is gonna be reset. | ||
// Reset the first focusable field since the form is gonna be reset. | ||
_this.firstField = undefined; | ||
_this.updateState(_this.state, function () { | ||
_this.applyStateChanges(newState, function () { | ||
if (!_this.mounted) { | ||
@@ -219,2 +203,3 @@ return; | ||
// Autofocus the form (if not configured otherwise) | ||
var autoFocus = _this.props.autoFocus; | ||
if (autoFocus) { | ||
@@ -225,3 +210,3 @@ // If `reset()` was called inside `onSubmit()`, then | ||
// are no longer disabled. | ||
if (_this.state.submitting) { | ||
if (_this.getState().submitting) { | ||
_this.focusableBeforeSubmit = _this.getFocusable(); | ||
@@ -233,4 +218,4 @@ } else { | ||
// Trigger each `<Field/>`'s `onChange()` handler. | ||
for (var _i2 = 0, _Object$keys2 = Object.keys(fields); _i2 < _Object$keys2.length; _i2++) { | ||
var _field = _Object$keys2[_i2]; | ||
for (var _i = 0, _Object$keys = Object.keys(_this.getState().fields); _i < _Object$keys.length; _i++) { | ||
var _field = _Object$keys[_i]; | ||
// If the field is still mounted. | ||
@@ -246,2 +231,4 @@ if (_this.fields[_field]) { | ||
} | ||
}, { | ||
resetForm: true | ||
}); | ||
@@ -278,5 +265,6 @@ }); | ||
_defineProperty(_assertThisInitialized(_this), "cleanUpRemovedFields", function () { | ||
var fields = _this.state.fields; | ||
for (var _i3 = 0, _Object$keys3 = Object.keys(fields); _i3 < _Object$keys3.length; _i3++) { | ||
var field = _Object$keys3[_i3]; | ||
var _this$getState3 = _this.getState(), | ||
fields = _this$getState3.fields; | ||
for (var _i2 = 0, _Object$keys2 = Object.keys(fields); _i2 < _Object$keys2.length; _i2++) { | ||
var field = _Object$keys2[_i2]; | ||
// Remove unmounted `<Field/>`s. | ||
@@ -314,3 +302,3 @@ if (fields[field] === 0) { | ||
// (i.e. submit is in progress) | ||
if (_this.state.submitting) { | ||
if (_this.getState().submitting) { | ||
return; | ||
@@ -344,6 +332,6 @@ } | ||
_defineProperty(_assertThisInitialized(_this), "clear", function (field) { | ||
return _this.set(field, undefined); | ||
return _this.set(field, EMPTY_VALUE); | ||
}); | ||
_defineProperty(_assertThisInitialized(_this), "get", function (field) { | ||
return _this.state.values[field]; | ||
return _this.getState().values[field]; | ||
}); | ||
@@ -376,2 +364,5 @@ _defineProperty(_assertThisInitialized(_this), "set", function (field, value) { | ||
}); | ||
_defineProperty(_assertThisInitialized(_this), "getState", function () { | ||
return _this.state.state; | ||
}); | ||
_defineProperty(_assertThisInitialized(_this), "setFormNode", function (node) { | ||
@@ -383,23 +374,3 @@ return _this.form = node; | ||
}); | ||
var _this$props3 = _this.props, | ||
_values = _this$props3.values, | ||
requiredMessage = _this$props3.requiredMessage, | ||
plugins = _this$props3.plugins, | ||
_wait = _this$props3.wait; | ||
_this.state = _objectSpread(_objectSpread({ | ||
resetCounter: 0 | ||
}, generateInitialFormState(_values, { | ||
submitting: _wait | ||
})), {}, { | ||
dispatch: _this.dispatch, | ||
onRegisterField: _this.onRegisterField, | ||
onUnregisterField: _this.onUnregisterField, | ||
getSubmittedValue: _this.getSubmittedValue, | ||
getRequiredMessage: function getRequiredMessage() { | ||
return requiredMessage; | ||
}, | ||
// These're used by `<List/>`. | ||
focus: _this.focus, | ||
getValues: _this.values | ||
}); | ||
var plugins = _this.props.plugins; | ||
_this.plugins = plugins.map(function (Plugin) { | ||
@@ -409,11 +380,6 @@ return new Plugin(function () { | ||
}, function () { | ||
return _this.state; | ||
return _this.getState(); | ||
}); | ||
}); | ||
for (var _iterator5 = _createForOfIteratorHelperLoose(_this.plugins), _step5; !(_step5 = _iterator5()).done;) { | ||
var plugin = _step5.value; | ||
if (plugin.initContext) { | ||
plugin.initContext(_this.state); | ||
} | ||
} | ||
_this.state = _this.getInitialContext(); | ||
return _this; | ||
@@ -434,4 +400,4 @@ } | ||
for (var _iterator6 = _createForOfIteratorHelperLoose(this.plugins), _step6; !(_step6 = _iterator6()).done;) { | ||
var plugin = _step6.value; | ||
for (var _iterator5 = _createForOfIteratorHelperLoose(this.plugins), _step5; !(_step5 = _iterator5()).done;) { | ||
var plugin = _step5.value; | ||
if (plugin.onMount) { | ||
@@ -455,2 +421,11 @@ plugin.onMount(); | ||
this.cleanUpRemovedFields(); | ||
for (var _iterator6 = _createForOfIteratorHelperLoose(this.plugins), _step6; !(_step6 = _iterator6()).done;) { | ||
var plugin = _step6.value; | ||
if (plugin.onUpdate) { | ||
plugin.onUpdate({ | ||
getState: this.getState, | ||
dispatch: this.dispatch | ||
}); | ||
} | ||
} | ||
} | ||
@@ -469,19 +444,130 @@ }, { | ||
}, { | ||
key: "updateState", | ||
value: function updateState(newState, callback) { | ||
key: "getInitialState", | ||
value: function getInitialState() { | ||
var initialState = this._getInitialState(); | ||
for (var _iterator8 = _createForOfIteratorHelperLoose(this.plugins), _step8; !(_step8 = _iterator8()).done;) { | ||
var plugin = _step8.value; | ||
if (plugin.getInitialState) { | ||
initialState = plugin.getInitialState(initialState); | ||
} | ||
} | ||
return initialState; | ||
} | ||
}, { | ||
key: "_getInitialState", | ||
value: function _getInitialState() { | ||
var _this$props3 = this.props, | ||
values = _this$props3.values, | ||
wait = _this$props3.wait; | ||
return generateInitialFormState(this.state ? this.getState().initialValues : values, { | ||
submitting: wait | ||
}); | ||
} | ||
}, { | ||
key: "getInitialContext", | ||
value: function getInitialContext() { | ||
var initialContext = this._getInitialContext(); | ||
// Add `context` functions by plugins. | ||
for (var _iterator9 = _createForOfIteratorHelperLoose(this.plugins), _step9; !(_step9 = _iterator9()).done;) { | ||
var plugin = _step9.value; | ||
if (plugin.getContextFunctions) { | ||
var contextFunctions = plugin.getContextFunctions(); | ||
for (var _i3 = 0, _Object$keys3 = Object.keys(contextFunctions); _i3 < _Object$keys3.length; _i3++) { | ||
var name = _Object$keys3[_i3]; | ||
initialContext[name] = contextFunctions[name]({ | ||
updateState: initialContext.updateState | ||
}); | ||
} | ||
} | ||
} | ||
return initialContext; | ||
} | ||
}, { | ||
key: "_getInitialContext", | ||
value: function _getInitialContext() { | ||
var _this$props4 = this.props, | ||
requiredMessage = _this$props4.requiredMessage, | ||
initialState = _this$props4.initialState; | ||
return { | ||
state: initialState || this.getInitialState(), | ||
// initialState, | ||
resetCounter: 0, | ||
dispatch: this.dispatch, | ||
updateState: this.updateState, | ||
onRegisterField: this.onRegisterField, | ||
onUnregisterField: this.onUnregisterField, | ||
getSubmittedValue: this.getSubmittedValue, | ||
getRequiredMessage: function getRequiredMessage() { | ||
return requiredMessage; | ||
}, | ||
// These're used by `<List/>`. | ||
focus: this.focus, | ||
getValues: this.values | ||
}; | ||
} | ||
}, { | ||
key: "applyStateChanges", | ||
value: function applyStateChanges(newState, callback) { | ||
var _ref3 = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}, | ||
resetForm = _ref3.resetForm; | ||
var prevContext = this.getContext(); | ||
var prevState = prevContext.state; | ||
// Call `onStateChange()` listener. | ||
var onStateChange = this.props.onStateChange; | ||
if (onStateChange) { | ||
onStateChange(newState); | ||
} | ||
// Run any field value watchers: | ||
// | ||
// See if any fields are watched. | ||
// If they are, see if their values have changed. | ||
// If they have, re-render the form after updating state. | ||
for (var _iterator8 = _createForOfIteratorHelperLoose(this.watchedFieldsList), _step8; !(_step8 = _iterator8()).done;) { | ||
var field = _step8.value; | ||
var prevValue = this.state.values[field]; | ||
var newValue = newState.values[field]; | ||
if (newValue !== prevValue) { | ||
// | ||
// This piece of code currently doesn't do anything | ||
// because `prevState` is always equal to `newState`. | ||
// Maybe it could be used in some future if new state objects would be created | ||
// instead of mutating the existing state object. | ||
// | ||
var shouldReRenderForm = false; | ||
for (var _iterator10 = _createForOfIteratorHelperLoose(this.watchedFieldsList), _step10; !(_step10 = _iterator10()).done;) { | ||
var field = _step10.value; | ||
if (newState.values[field] !== prevState.values[field]) { | ||
// Re-render the form after updating state. | ||
newState = _objectSpread({}, newState); | ||
shouldReRenderForm = true; | ||
break; | ||
} | ||
} | ||
// Update state. | ||
this.setState(newState, callback); | ||
// | ||
// A `React.Component` always re-renders on `this.setState()`, | ||
// even if the `state` hasn't changed. | ||
// The re-rendering of the `<Form/>` is used to re-render | ||
// the `<Field/`>s with the updated `value`s. | ||
// This could potentially result in slower performance | ||
// on `<Form/>`s with a lots of `<Field/>`s | ||
// (maybe hundreds or something like that?) | ||
// but on regular `<Form/>`s I didn't notice any lag. | ||
// A possible performance optimization could be | ||
// not calling `this.setState()` for `<Form/>` re-rendering | ||
// and instead calling something like `this.forceUpdate()` | ||
// on just the exact `<Field/>` that called `context.dispatch(action)`. | ||
// | ||
// `this.setState()` is called on `this.state` rather than creating | ||
// a new `state`, because `this.state` is used as the `context` property | ||
// for `React.Context`, so if `state` reference changes, the whole form | ||
// gets re-rendered, and that's not something that should be done | ||
// unless any "field value watchers" have been triggered due to a changed field value. | ||
// | ||
var newContext = shouldReRenderForm ? _objectSpread({}, prevContext) : prevContext; | ||
newContext.state = newState; | ||
if (resetForm) { | ||
// Changing the `resetCounter` property results in a complete re-mounting | ||
// of the `<form/>`, including all of the `<Field/>`s. | ||
newContext.resetCounter = getNext(newContext.resetCounter); | ||
} | ||
this.setState(newContext, callback); | ||
} | ||
@@ -500,4 +586,4 @@ | ||
function applyPluginValueTransforms(values) { | ||
for (var _iterator9 = _createForOfIteratorHelperLoose(this.plugins), _step9; !(_step9 = _iterator9()).done;) { | ||
var plugin = _step9.value; | ||
for (var _iterator11 = _createForOfIteratorHelperLoose(this.plugins), _step11; !(_step11 = _iterator11()).done;) { | ||
var plugin = _step11.value; | ||
if (plugin.getValues) { | ||
@@ -514,6 +600,6 @@ values = plugin.getValues(values); | ||
value: function searchForInvalidField() { | ||
var _this$state3 = this.state, | ||
fields = _this$state3.fields, | ||
values = _this$state3.values, | ||
errors = _this$state3.errors; | ||
var _this$getState4 = this.getState(), | ||
fields = _this$getState4.fields, | ||
values = _this$getState4.values, | ||
errors = _this$getState4.errors; | ||
@@ -549,5 +635,5 @@ // Re-run `validate()` for each field. | ||
var scrollDuration = this.props.scrollDuration; | ||
var _this$state4 = this.state, | ||
fields = _this$state4.fields, | ||
values = _this$state4.values; | ||
var _this$getState5 = this.getState(), | ||
fields = _this$getState5.fields, | ||
values = _this$getState5.values; | ||
@@ -604,5 +690,5 @@ // Are there any invalid fields. | ||
value: function getSubmittedValues() { | ||
var _this$state5 = this.state, | ||
fields = _this$state5.fields, | ||
values = _this$state5.values; | ||
var _this$getState6 = this.getState(), | ||
fields = _this$getState6.fields, | ||
values = _this$getState6.values; | ||
// Get only "registered" (non-removed) field values. | ||
@@ -736,8 +822,13 @@ var fieldValues = getValues(values, fields); | ||
}, { | ||
key: "getContext", | ||
value: function getContext() { | ||
return this.state; | ||
} | ||
}, { | ||
key: "render", | ||
value: function render() { | ||
var children = this.props.children; | ||
var _this$state6 = this.state, | ||
resetCounter = _this$state6.resetCounter, | ||
submitting = _this$state6.submitting; | ||
var resetCounter = this.getContext.resetCounter; | ||
var _this$getState7 = this.getState(), | ||
submitting = _this$getState7.submitting; | ||
return /*#__PURE__*/React.createElement("form", _extends({ | ||
@@ -749,3 +840,3 @@ key: resetCounter, | ||
}), /*#__PURE__*/React.createElement(Context.Provider, { | ||
value: this.state | ||
value: this.getContext() | ||
}, typeof children === 'function' ? /*#__PURE__*/React.createElement(Children, { | ||
@@ -770,3 +861,5 @@ values: this.mounted ? this.values() : undefined, | ||
onAfterSubmit: PropTypes.func, | ||
onStateChange: PropTypes.func, | ||
onAbandon: PropTypes.func, | ||
initialState: PropTypes.object, | ||
values: PropTypes.object, | ||
@@ -794,12 +887,12 @@ autoFocus: PropTypes.bool.isRequired, | ||
export { Form as default }; | ||
function Children(_ref3) { | ||
var values = _ref3.values, | ||
reset = _ref3.reset, | ||
set = _ref3.set, | ||
clear = _ref3.clear, | ||
scroll = _ref3.scroll, | ||
focus = _ref3.focus, | ||
watch = _ref3.watch, | ||
submitting = _ref3.submitting, | ||
children = _ref3.children; | ||
function Children(_ref4) { | ||
var values = _ref4.values, | ||
reset = _ref4.reset, | ||
set = _ref4.set, | ||
clear = _ref4.clear, | ||
scroll = _ref4.scroll, | ||
focus = _ref4.focus, | ||
watch = _ref4.watch, | ||
submitting = _ref4.submitting, | ||
children = _ref4.children; | ||
return children({ | ||
@@ -829,5 +922,5 @@ values: values, | ||
var initialValues = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
var _ref4 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, | ||
_ref4$submitting = _ref4.submitting, | ||
submitting = _ref4$submitting === void 0 ? false : _ref4$submitting; | ||
var _ref5 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, | ||
_ref5$submitting = _ref5.submitting, | ||
submitting = _ref5$submitting === void 0 ? false : _ref5$submitting; | ||
return { | ||
@@ -855,12 +948,15 @@ // `mounted`/`unmounted` counters for each form field. | ||
export var contextPropType = PropTypes.shape({ | ||
fields: PropTypes.object.isRequired, | ||
values: PropTypes.object.isRequired, | ||
initialValues: PropTypes.object.isRequired, | ||
errors: PropTypes.object.isRequired, | ||
validationErrors: PropTypes.object.isRequired, | ||
showErrors: PropTypes.object.isRequired, | ||
submitting: PropTypes.bool.isRequired, | ||
state: PropTypes.shape({ | ||
fields: PropTypes.object.isRequired, | ||
values: PropTypes.object.isRequired, | ||
initialValues: PropTypes.object.isRequired, | ||
errors: PropTypes.object.isRequired, | ||
validationErrors: PropTypes.object.isRequired, | ||
showErrors: PropTypes.object.isRequired, | ||
latestFocusedField: PropTypes.string, | ||
submitting: PropTypes.bool.isRequired | ||
}).isRequired, | ||
updateState: PropTypes.func.isRequired, | ||
onRegisterField: PropTypes.func.isRequired, | ||
onUnregisterField: PropTypes.func.isRequired, | ||
onRegisterList: PropTypes.func.isRequired, | ||
getSubmittedValue: PropTypes.func.isRequired, | ||
@@ -870,4 +966,7 @@ focus: PropTypes.func.isRequired, | ||
getRequiredMessage: PropTypes.func.isRequired, | ||
getValues: PropTypes.func.isRequired | ||
getValues: PropTypes.func.isRequired, | ||
// These get added by `ListPlugin`. | ||
onRegisterList: PropTypes.func.isRequired, | ||
onListStateChange: PropTypes.func.isRequired | ||
}); | ||
//# sourceMappingURL=form.js.map |
function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); } | ||
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } | ||
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
@@ -20,3 +18,2 @@ function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } } | ||
import PropTypes from 'prop-types'; | ||
import createContext from 'create-react-context'; | ||
import { Context } from './form'; | ||
@@ -38,10 +35,10 @@ import { getFieldName } from './plugins/ListPlugin.utility'; | ||
_this = _super.call(this, props); | ||
_defineProperty(_assertThisInitialized(_this), "onReset", function () { | ||
_this.setState(_this.getInitialItemsState()); | ||
_defineProperty(_assertThisInitialized(_this), "reset", function () { | ||
_this.setListState(_this.getInitialListState()); | ||
}); | ||
_defineProperty(_assertThisInitialized(_this), "getFieldName", function (i, name) { | ||
if (typeof i !== 'number') { | ||
throw new Error('Each `<Feild/>` in a `<List/>` must have an `i` property'); | ||
_defineProperty(_assertThisInitialized(_this), "getFieldName", function (itemId, name) { | ||
if (typeof itemId !== 'number') { | ||
throw new Error('Each `<Feild/>` in a `<List/>` must have an `item` property'); | ||
} | ||
return getFieldName(_this.props.name, i, name); | ||
return getFieldName(_this.props.name, itemId, name); | ||
}); | ||
@@ -55,20 +52,23 @@ _defineProperty(_assertThisInitialized(_this), "onRegisterField", function (name) { | ||
var context = _this.props.context; | ||
var _this$state = _this.state, | ||
items = _this$state.items, | ||
itemsCounter = _this$state.itemsCounter; | ||
_this.setState({ | ||
itemsCounter: itemsCounter + 1, | ||
items: items.concat([itemsCounter]) | ||
var _this$getListState = _this.getListState(), | ||
items = _this$getListState.items, | ||
maxItemId = _this$getListState.maxItemId; | ||
var itemId = maxItemId + 1; | ||
_this.setListState({ | ||
maxItemId: itemId, | ||
items: items.concat([itemId]) | ||
}, function () { | ||
var name = _this.props.name; | ||
if (_this.firstFieldName) { | ||
context.focus("".concat(name, ":").concat(itemsCounter, ":").concat(_this.firstFieldName)); | ||
context.focus(_this.getFieldName(itemId, _this.firstFieldName)); | ||
} | ||
}); | ||
}); | ||
_defineProperty(_assertThisInitialized(_this), "remove", function (i) { | ||
var items = _this.state.items; | ||
_this.setState({ | ||
_defineProperty(_assertThisInitialized(_this), "remove", function (itemId) { | ||
var _this$getListState2 = _this.getListState(), | ||
items = _this$getListState2.items, | ||
maxItemId = _this$getListState2.maxItemId; | ||
_this.setListState({ | ||
maxItemId: maxItemId, | ||
items: items.filter(function (_) { | ||
return _ !== i; | ||
return _ !== itemId; | ||
}) | ||
@@ -78,13 +78,15 @@ }); | ||
_defineProperty(_assertThisInitialized(_this), "map", function (func) { | ||
var items = _this.state.items; | ||
return items.map(function (i) { | ||
return func(i); | ||
var _this$getListState3 = _this.getListState(), | ||
items = _this$getListState3.items; | ||
return items.map(function (item) { | ||
return func(item); | ||
}); | ||
}); | ||
_this.state = _objectSpread({ | ||
context: { | ||
getFieldName: _this.getFieldName, | ||
onRegisterField: _this.onRegisterField | ||
} | ||
}, _this.getInitialItemsState()); | ||
_this.state = { | ||
listContext: { | ||
getFieldNameInsideList: _this.getFieldName, | ||
onRegisterFieldInsideList: _this.onRegisterField | ||
}, | ||
state: _this.getInitialListState() | ||
}; | ||
return _this; | ||
@@ -99,27 +101,70 @@ } | ||
context.onRegisterList(name, { | ||
onReset: this.onReset | ||
onReset: this.reset, | ||
state: this.getListState() | ||
}); | ||
} | ||
}, { | ||
key: "componentWillUnmount", | ||
value: function componentWillUnmount() { | ||
var _this$props2 = this.props, | ||
name = _this$props2.name, | ||
context = _this$props2.context; | ||
context.onUnregisterList(name); | ||
} | ||
}, { | ||
key: "getInitialItems", | ||
value: function getInitialItems() { | ||
var _this$props2 = this.props, | ||
context = _this$props2.context, | ||
name = _this$props2.name, | ||
count = _this$props2.count; | ||
if (context.initialValues[name]) { | ||
return createIndexArray(context.initialValues[name].length); | ||
var _this$props3 = this.props, | ||
context = _this$props3.context, | ||
name = _this$props3.name, | ||
count = _this$props3.count; | ||
// console.error(`[easy-react-form] \`initialState\` doesn't include the state for list "${name}"`) | ||
var initialListValue = context.state.initialValues[name]; | ||
if (initialListValue) { | ||
return createItemIdsArray(initialListValue.length); | ||
} | ||
return createIndexArray(count); | ||
return createItemIdsArray(count); | ||
} | ||
}, { | ||
key: "getInitialItemsState", | ||
value: function getInitialItemsState() { | ||
key: "getInitialListState", | ||
value: function getInitialListState() { | ||
// Get list initial state from the form's `initialState`. | ||
var _this$props4 = this.props, | ||
context = _this$props4.context, | ||
name = _this$props4.name; | ||
if (context.state.lists[name]) { | ||
if (context.state.listInstanceCounters[name] > 0) { | ||
return context.state.lists[name]; | ||
} | ||
} | ||
// Create list initial state. | ||
var items = this.getInitialItems(); | ||
return { | ||
items: items, | ||
itemsCounter: items.length | ||
maxItemId: items[items.length - 1] | ||
}; | ||
} | ||
}, { | ||
key: "getListState", | ||
value: function getListState() { | ||
return this.state.state; | ||
} | ||
}, { | ||
key: "setListState", | ||
value: function setListState(state, callback) { | ||
var _this$props5 = this.props, | ||
context = _this$props5.context, | ||
name = _this$props5.name; | ||
context.onListStateChange(name, state); | ||
this.setState({ | ||
state: state | ||
}, callback); | ||
} | ||
}, { | ||
key: "getListContext", | ||
value: function getListContext() { | ||
return this.state.listContext; | ||
} | ||
}, { | ||
key: "render", | ||
@@ -129,6 +174,5 @@ value: function render() { | ||
var children = this.props.children; | ||
var listContext = this.state.context; | ||
return /*#__PURE__*/React.createElement(Context.Consumer, null, function (context) { | ||
return /*#__PURE__*/React.createElement(ListContext.Provider, { | ||
value: listContext | ||
value: _this2.getListContext() | ||
}, children({ | ||
@@ -153,4 +197,4 @@ map: _this2.map, | ||
}; | ||
export var ListContext = createContext(); | ||
function createIndexArray(size) { | ||
export var ListContext = /*#__PURE__*/React.createContext(); | ||
function createItemIdsArray(size) { | ||
var array = new Array(size); | ||
@@ -165,5 +209,5 @@ var i = 0; | ||
export var listContextPropType = PropTypes.shape({ | ||
getFieldName: PropTypes.func.isRequired, | ||
onRegisterField: PropTypes.func.isRequired | ||
getFieldNameInsideList: PropTypes.func.isRequired, | ||
onRegisterFieldInsideList: PropTypes.func.isRequired | ||
}); | ||
//# sourceMappingURL=list.js.map |
function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); } | ||
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } | ||
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
@@ -8,3 +10,3 @@ function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } } | ||
function _toPrimitive(input, hint) { if (_typeof(input) !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (_typeof(res) !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } | ||
import { getListValue, convertListValues } from './ListPlugin.utility'; | ||
import { getListValue, convertListValues, removeListAction } from './ListPlugin.utility'; | ||
var ListPlugin = /*#__PURE__*/function () { | ||
@@ -16,13 +18,61 @@ function ListPlugin() { | ||
_createClass(ListPlugin, [{ | ||
key: "initContext", | ||
value: function initContext(context) { | ||
key: "getContextFunctions", | ||
value: function getContextFunctions() { | ||
var _this = this; | ||
context.onRegisterList = function (name, _ref) { | ||
var onReset = _ref.onReset; | ||
_this.lists[name] = { | ||
onReset: onReset | ||
}; | ||
return { | ||
onRegisterList: function onRegisterList(_ref) { | ||
var updateState = _ref.updateState; | ||
return function (name, _ref2) { | ||
var onReset = _ref2.onReset, | ||
listState = _ref2.state; | ||
_this.lists[name] = { | ||
onReset: onReset | ||
}; | ||
updateState(function (state) { | ||
if (state.listInstanceCounters[name] === undefined) { | ||
state.listInstanceCounters[name] = 1; | ||
} else { | ||
state.listInstanceCounters[name]++; | ||
} | ||
state.lists[name] = listState; | ||
}); | ||
}; | ||
}, | ||
onUnregisterList: function onUnregisterList(_ref3) { | ||
var updateState = _ref3.updateState; | ||
return function (name) { | ||
_this.lists[name] = undefined; | ||
// Doesn't reset the list's `state` in form state | ||
// because the list might get re-mounted at some other position | ||
// in the document during the ongoing React re-render. | ||
updateState(function (state) { | ||
// Doesn't create a new `state` object. | ||
// Instead it "mutates" the original one. | ||
// The rationale is provided in `actions.js`. | ||
state.listInstanceCounters[name]--; | ||
}); | ||
}; | ||
}, | ||
onListStateChange: function onListStateChange(_ref4) { | ||
var updateState = _ref4.updateState; | ||
return function (name, newState) { | ||
updateState(function (state) { | ||
// Doesn't create a new `state` object. | ||
// Instead it "mutates" the original one. | ||
// The rationale is provided in `actions.js`. | ||
state.lists[name] = newState; | ||
}); | ||
}; | ||
} | ||
}; | ||
} | ||
}, { | ||
key: "getInitialState", | ||
value: function getInitialState(state) { | ||
return _objectSpread(_objectSpread({}, state), {}, { | ||
lists: {}, | ||
listInstanceCounters: {} | ||
}); | ||
} | ||
}, { | ||
key: "getValues", | ||
@@ -43,3 +93,4 @@ value: function getValues(values) { | ||
if (this.lists[name]) { | ||
var fields = form.state.fields; | ||
var _form$getState = form.getState(), | ||
fields = _form$getState.fields; | ||
for (var _i = 0, _Object$keys = Object.keys(fields); _i < _Object$keys.length; _i++) { | ||
@@ -55,2 +106,17 @@ var field = _Object$keys[_i]; | ||
} | ||
}, { | ||
key: "onUpdate", | ||
value: function onUpdate(_ref5) { | ||
var getState = _ref5.getState, | ||
dispatch = _ref5.dispatch; | ||
var _getState = getState(), | ||
listInstanceCounters = _getState.listInstanceCounters; | ||
for (var _i2 = 0, _Object$keys2 = Object.keys(listInstanceCounters); _i2 < _Object$keys2.length; _i2++) { | ||
var name = _Object$keys2[_i2]; | ||
// Remove unmounted `<List/>`s. | ||
if (listInstanceCounters[name] === 0) { | ||
dispatch(removeListAction(name)); | ||
} | ||
} | ||
} | ||
}]); | ||
@@ -57,0 +123,0 @@ return ListPlugin; |
@@ -95,2 +95,8 @@ function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); } | ||
} | ||
export var removeListAction = function removeListAction(name) { | ||
return function (state) { | ||
delete state.lists[name]; | ||
delete state.listInstanceCounters[name]; | ||
}; | ||
}; | ||
//# sourceMappingURL=ListPlugin.utility.js.map |
@@ -14,3 +14,3 @@ function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); } | ||
return /*#__PURE__*/React.createElement(props.component, _objectSpread(_objectSpread({}, getPassThroughProps(props, Submit.propTypes)), {}, { | ||
wait: context.submitting | ||
wait: context.state.submitting | ||
})); | ||
@@ -17,0 +17,0 @@ }); |
{ | ||
"name": "easy-react-form", | ||
"version": "1.3.4", | ||
"version": "2.0.0-beta.2", | ||
"description": "Simple, fast and easy-to-use React Form.", | ||
@@ -9,9 +9,7 @@ "main": "index.commonjs.js", | ||
"peerDependencies": { | ||
"react": ">=0.14.0", | ||
"react-dom": ">=0.14.0" | ||
"react": ">=0.18.2.0", | ||
"react-dom": ">=0.18.2.0" | ||
}, | ||
"dependencies": { | ||
"create-react-context": "^0.2.2", | ||
"prop-types": "^15.7.2", | ||
"react-create-ref": "^1.0.0", | ||
"scroll-into-view-if-needed": "^2.2.24", | ||
@@ -32,3 +30,7 @@ "smooth-scroll-into-view-if-needed": "^1.1.27" | ||
"react-dom": "^15.2.1", | ||
"rimraf": "^2.5.0" | ||
"rimraf": "^2.5.0", | ||
"rollup": "^2.79.1", | ||
"rollup-plugin-commonjs": "^10.1.0", | ||
"rollup-plugin-node-resolve": "^5.2.0", | ||
"rollup-plugin-terser": "^7.0.2" | ||
}, | ||
@@ -42,3 +44,4 @@ "scripts": { | ||
"build-es6-modules": "better-npm-run build-es6-modules", | ||
"build": "npm-run-all clean-for-build build-commonjs-modules build-es6-modules", | ||
"build:browser": "rollup --config rollup.config.js", | ||
"build": "npm-run-all clean-for-build build-commonjs-modules build-es6-modules build:browser", | ||
"prepublish": "npm-run-all build test" | ||
@@ -45,0 +48,0 @@ }, |
@@ -128,2 +128,6 @@ # easy-react-form | ||
* `initialState : object` — One can pass a previously stored form state in order to restore the form to the state it was at that point in time. | ||
* `onStateChange(newState : object)` — Will get called whenever a form's state changes. | ||
* `wait : Boolean` — The initial `wait` state of the submit button. Can be used when the form is "loading" on first render. | ||
@@ -344,13 +348,13 @@ | ||
<React.Fragment> | ||
{items.map((i) => ( | ||
<React.Fragment> | ||
{items.map((itemId) => ( | ||
<React.Fragment key={itemId}> | ||
<Field | ||
i={i} | ||
item={itemId} | ||
name="firstName" | ||
.../> | ||
<Field | ||
i={i} | ||
item={itemId} | ||
name="lastName" | ||
.../> | ||
<button onClick={() => items.remove(i)}> | ||
<button type="button" onClick={() => items.remove(itemId)}> | ||
Remove | ||
@@ -360,3 +364,3 @@ </button> | ||
))} | ||
<button onClick={items.add}> | ||
<button type="button" onClick={() => items.add()}> | ||
Add | ||
@@ -363,0 +367,0 @@ </button> |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
461884
5
49
4358
423
16
1
- Removedcreate-react-context@^0.2.2
- Removedreact-create-ref@^1.0.0
- Removedasap@2.0.6(transitive)
- Removedcore-js@1.2.7(transitive)
- Removedcreate-react-context@0.2.3(transitive)
- Removedencoding@0.1.13(transitive)
- Removedfbjs@0.8.18(transitive)
- Removedgud@1.0.0(transitive)
- Removediconv-lite@0.6.3(transitive)
- Removedis-stream@1.1.0(transitive)
- Removedisomorphic-fetch@2.2.1(transitive)
- Removednode-fetch@1.7.3(transitive)
- Removedpromise@7.3.1(transitive)
- Removedreact@16.14.019.0.0(transitive)
- Removedreact-create-ref@1.0.1(transitive)
- Removedreact-dom@19.0.0(transitive)
- Removedsafer-buffer@2.1.2(transitive)
- Removedscheduler@0.25.0(transitive)
- Removedsetimmediate@1.0.5(transitive)
- Removedua-parser-js@0.7.39(transitive)
- Removedwhatwg-fetch@3.6.20(transitive)