React integration¶
The @ostack.tech/kform-react package
provides middleware to interact with KForm from React.
<Form>¶
KForm-React forms can be declared via the <Form> component. This component expects, amongst other
optional properties, a mandatory schema containing the schema of the form, which is used to
instantiate a FormManager used to manage the whole form:
import { Form } from "@ostack.tech/kform-react";
import { BusTripFormSchema } from "bus-trip-form-shared";
export function BusTripForm() {
return (
<Form
schema={BusTripFormSchema.get()}
initialValue={/* … */}
externalContexts={/* … */}
validationMode={/* … */}
onSubmit={async (value, warnings) => {
const result = await submitBusTripForm(value, warnings);
return result.success ? result.redirectUrl : result.issues;
}}
onSuccessfulSubmit={(redirectUrl) => {
window.location.href = redirectUrl;
}}
>
<button type="submit">Submit</button>
{/* … */}
</Form>
);
}
The <Form> component supports the following main properties:
schema: The schema of the form. Typically defined in a shared Kotlin multiplatform module and exposed to the JavaScript world via@JsExport.initialValue: The initial value of the form, as expected by theFormManager’s constructor.-
externalContexts: External contexts used by the form’s validations or computed values. Expects an object mapping context names to their values.The value of this property may be dynamically updated. The component uses the form manager’s
setExternalContext/removeExternalContextAPI to update the external contexts as appropriate. -
validationMode: The mode of validation used by the form manager. See the Validation modes section for more details.The value of this property may be dynamically updated. The component uses the form manager’s
setValidationModeAPI to update the validation mode as appropriate. -
confirmUnloadWhenDirty: Whentrue(the default in production), the browser will ask the user for confirmation when attempting to unload the form (e.g. closing the page) while the form is dirty. -
onSubmit: Function called during submission when the form value to submit is locally valid (no validation errors were found) or ifvalidateOnSubmitis set tofalse. This function may (possibly asynchronously) return two types of values, or throw an error:- Function returns external validation issues: the submission is considered invalid (even if all
returned issues are warnings). Returned issues that aren’t already in the form manager are
added to it via
addExternalIssues.onInvalidSubmitis subsequently called with the returned issues. - Function returns any other value: the submission is considered successful.
onSuccessfulSubmitwill be called with the returned value after setting the form as pristine (unlesssetPristineOnSuccessfulSubmitis set tofalse). - Function throws: the submission is considered to have failed.
onFailedSubmitwill be called with the thrown error.
- Function returns external validation issues: the submission is considered invalid (even if all
returned issues are warnings). Returned issues that aren’t already in the form manager are
added to it via
-
onInvalidSubmit: Function called during submission when the form value to submit is locally invalid (local validation errors were found), or afteronSubmitif it returns external validation issues. -
onSuccessfulSubmit: Function called after a successful submission with the result ofonSubmit(when this result isn’t a list of issues).Unless
setPristineOnSuccessfulSubmitis set tofalse, the form will be pristine by the time this function runs, meaning that redirections that cause the form to be unloaded should happen within this function (so that the form unload confirmation isn’t triggered). -
onFailedSubmit: Function called after a submission that resulted in an error. onReset: Function called when resetting the form, before the form is actually reset. Preventing the default behaviour on the received event (viaevent.preventDefault()) will prevent the form from being reset.setTouchedOnSubmit: Whether to mark the whole form as touched before submitting. Defaults totrue.validateOnSubmit: Whether to validate the form before submitting. Defaults totrue. When set tofalse,onSubmitis always called andonInvalidSubmitis only called ifonSubmitreturns external validation issues.setPristineOnSuccessfulSubmit: Whether to set the whole form as pristine after a successful submission, but before invokingonSuccessfulSubmit. Defaults totrue.-
convertExternalIssuesTableRowIndicesToIds: KForm table rows have unique non-sequential identifiers. Depending on how you choose to serialise your form, these identifiers might be discarded. In such a scenario, upon deserialisation on the server, the identifiers will typically become sequential (i.e. index-based). This means that, if issues were found server-side within a table, the paths will contain identifiers not matching the client-side ones.By setting this property to
true, the component will convert table row indices into their local client-side identifiers before processing the external issues returned byonSubmit.
useFormManager¶
The form’s FormManager instance is exposed via the useFormManager hook, as long
as the hook is called within a <Form> component:
useController¶
Controllers leverage the form manager to provide simple-to-use React APIs for accessing a form’s value and its state.
The useController hook is the foundation of all hooks which expose controllers:
The provided path is resolved against the path currently in scope and must only contain identifiers
and an optional trailing recursive wildcard fragment (/**). When a trailing recursive wildcard is
provided, it indicates that the controller should also react to changes in descendants.
The hook receives, as its second argument, an optional object supporting the following properties:
formManager: The form manager instance to use. Defaults to the one in scope.enabled: abooleanindicating whether the controller should be enabled.onInitialized(state): callback function called with the controller’s state when it is initialised.onUninitialized(state): callback function called whenever the controller is uninitialised.onFormManagerEvent(event, state): callback function called whenever the form manager emits an event matching the controller’s path.onValueChange(event, state): callback function called whenever the controller’s value changes.onValidationStatusChange(event, state): callback function called whenever the controller’s validation status changes.onDisplayStatusChange(event, state): callback function called whenever the controller’s display status changes.onDirtyStatusChange(event, state): callback function called whenever the controller’s dirty status changes.onTouchedStatusChange(event, state): callback function called whenever the controller’s touched status changes.
Whereas the returned controller object contains the following functions:
getState(): Returns the current state of controller. Should not be called during render.subscribe(selector, listener, options?): Function used to subscribe to changes in the controller’s state. Returns an unsubscribe function.useState(selector?, options?): Hook providing access to the controller’s state at render-time.useFormManager(): Hook which returns the form manager being used by the controller.useSchema(): Hook which returns the schema of the value being controlled.usePath(): Hook which returns the path of the value being controlled by this controller. This path will not contain any provided recursive wildcards.useSchemaPath(): Hook which returns the schema path of the value being controlled.useObservingDescendants(): Hook which returns whether the controller is currently observing descendants.useInitialized(): Hook which returns whether the controller has been initialised.useExists(): Hook which returns whether a value exists at the path being controlled, orundefinedwhen the controller is not initialised.-
useValue(): Hook which returns the form value being controlled, orundefinedwhen the controller is not initialised.Note that, when observing descendants, this hook will cause a component to rerender when a descendant is changed, even if the identity of the value hasn’t changed.
-
useDirty(): Hook which returns whether the value being controlled is dirty, orundefinedwhen the controller is not initialised. useTouched(): Hook which returns whether the value being controlled is touched, orundefinedwhen the controller is not initialised.useIssues(): Hook which returns the issues of the value being controlled, orundefinedwhen the controller is not initialised.useValidationStatus(): Hook which returns the validation status of the value being controlled, orundefinedwhen the controller is not initialised.useDisplayStatus(): Hook which returns the display status of the value being controlled, orundefinedwhen the controller is not initialised.get(callback)/get(path, callback):FormManager.getwith an optional path relative to the controller’s.getClone(path?):FormManager.getClonewith an optional path relative to the controller’s.set(value)/set(path, value):FormManager.setwith an optional path relative to the controller’s.reset(path?):FormManager.resetwith an optional path relative to the controller’s.remove(path?):FormManager.removewith an optional path relative to the controller’s.validate(path?):FormManager.validatewith an optional path relative to the controller’s.setDirty(path?):FormManager.setDirtywith an optional path relative to the controller’s.setPristine(path?):FormManager.setPristinewith an optional path relative to the controller’s.setTouched(path?):FormManager.setTouchedwith an optional path relative to the controller’s.setUntouched(path?):FormManager.setUntouchedwith an optional path relative to the controller’s.
The controller’s state is a plain object containing the following properties:
formManager: Form manager being used by the controller.schema: Schema of the form value being controlled.path: Path of the form value being controlled.schemaPath: Schema path of the form value being controlled.observingDescendants: Whether the controller is observing descendants.initialized: Whether the controller has been initialised.exists: Whether the form value being controlled exists.value: Form value.dirty: Whether the form value being controlled is dirty.touched: Whether the form value being controlled has been touched.issues: Validation issues associated with the form value being controlled.validationStatus: Validation status of the form value being controlled. One of"unvalidated","validating","validated", or"validatedExceptionally".displayStatus: Display status of the form value being controlled. One of"valid","error", or"warning".
useFormController¶
The form’s own controller (which controls the root value of the form) can be accessed via the
useFormController() hook:
It returns a controller object which extends a useController controller with
(amonst others) a submit function:
submit(options?): Function which can be used to programmatically submit the form.
useFormatter¶
The useFormatter hook extends useController with formatting capabilities.
Besides all options accepted by useController, the options object also accepts
the following properties:
-
setFormattedValue(formattedValue, state): Function used to set the formatted value. AuseStatedispatcher can be used to save the output value within React state.The passed value results from calling
formaton the form manager value. Ifformatis not defined, then the form manager value is passed directly. -
format(value, state): Format function that transforms the form manager value into a value to be output. The passedvalueisundefinedwhen the form manager value does not exist.
The returned controller object extends the useController controller with the
following function:
useFormattedValue(): Hook which returns whether the formatted value.
The controller’s state extends the useController controller’s state with the
following property:
formattedValue: Value after being formatted.
useFormattedValue¶
The useFormattedValue hook is a convenience wrapper around useFormatter,
providing the formatted value directly as state:
Usage of this hook is recommended whenever there is a need to access a value of the form at render-time.
<FormattedValue>¶
The <FormattedValue> component is another convenience wrapper around
useFormatter for displaying formatted values.
useInput¶
The useInput hook extends useFormatter for integration with form controls such
as <input>, so that values are written to the form manager as the user interacts with the form
control, and vice versa:
It accepts all options accepted by useController, as well as the following
properties:
-
setFormattedValue(formattedValue, state, element): Function used to set the formatted value in the form control. The passed value results from callingformaton the form manager value. Ifformatis not defined, then the form manager value is passed directly.By default, this function is capable of setting the values of
<input>,<select>, and<textarea>elements. -
format(value, state, element): Function that formats the form manager value into a value to be set in the form control. The passedvalueisundefinedwhen the form manager value does not exist. parse(value, state, element): Function used to parse the form control value into a value to be set in the form manager.
The returned controller extends the useFormatter controller with the following
extra property:
-
inputProps: Object containing the properties to be passed to the form control:name: Name of the form control.disabled: Whether the form control should be disabled. This istruewhile the form is being submitted or when the value does not exist.readOnly: Whether the form control should be read-only. This istruewhen the schema associated with this control is computed.ref: Reference to the form control.-
onChange: Function to run whenever the value of the form control changes. This function can receive either a change event with a standard HTML<input>,<select>, or<textarea>as target or a value directly.This function sets the new value in the form manager and marks the value as “dirty”.
-
onBlur: Function to run whenever the input element is blurred. This function marks the form value as “touched”.
The following example shows how to use the useInput hook to create a custom text input component:
export function TextInput({ path, className, disabled, readOnly, ref, ...otherProps }) {
const { useSchema, useInitialized, useIssues, useDisplayStatus, inputProps } = useInput(path);
const { restrictions } = useSchema().typeInfo;
const initialized = useInitialized();
const displayStatus = useDisplayStatus();
return (
<input
className={classNames(displayStatus, className)}
name={inputProps.name}
disabled={inputProps.disabled || !initialized || disabled}
readOnly={inputProps.readOnly || readOnly}
required={restrictions.required === true}
minLength={restrictions.minLength}
maxLength={restrictions.maxLength}
pattern={restrictions.pattern}
aria-invalid={displayStatus === "error"}
onChange={combineEventHandlers(inputProps.onChange, onChange)}
onBlur={combineEventHandlers(inputProps.onBlur, onBlur)}
ref={combineRefs(inputProps.ref, ref)}
{...otherProps}
/>
);
}
useFileInput¶
The useFileInput hook extends useInput for integration with values with a
FileSchema:
An error is thrown if the path does not point to a value with a
FileSchema.
This hook accepts the same options as useInput, but instead of format and parse,
it instead accepts properties that deal with JavaScript
File objects (abstracting away the usage
of KForm Files):
formatFromJsFile(fileValue, state, element): Function that formats a JavaScriptFileinto a value to be set in the form control.parseToJsFile(formattedValue, state, element): Function used to parse the form control value into a JavaScriptFileto be set in the form manager.
useListableInput¶
The useListableInput hook extends useInput for integration with values with a
“listable” schema
(ListSchema or
TableSchema):
An error is thrown if the path does not point to a value with a “listable” schema.
This hook accepts the same options as useInput, but instead of format and parse,
it instead accepts properties that deal with arrays (abstracting away the usage of the different
listable types):
formatFromArray(arrayValue, state, element): Function that formats an array into a value to be set in the form control.parseToArray(formattedValue, state, element): Function used to parse the form control value into an array to be set in the form manager.
useNumericInput¶
The useNumericInput hook extends useInput for integration with values with a
“numeric” schema
(ByteSchema,
CharSchema,
ShortSchema,
IntSchema,
LongSchema,
FloatSchema,
DoubleSchema,
BigIntegerSchema,
BigDecimalSchema,
or StringSchema):
An error is thrown if the path does not point to a value with a “numeric” schema.
This hook accepts the same options as useInput, but instead of format and parse,
it instead accepts properties that deal with numeric strings (abstracting away the usage of the
different numeric types):
formatFromString(stringValue, state, element): Function that formats a numeric string into a value to be set in the form control.parseToString(formattedValue, state, element): Function used to parse the form control value into a numeric string to be set in the form manager.
useTemporalInput¶
The useTemporalInput hook extends useInput for integration with values with a
“temporal” schema
(IntantSchema,
LocalDateSchema,
or
LocalDateTimeSchema):
An error is thrown if the path does not point to a value with a “temporal” schema.
This hook accepts the same options as useInput, but instead of format and parse,
it instead accepts properties that deal with ISO 8601
strings (abstracting away the usage of the different temporal types):
formatFromString(stringValue, state, element): Function that formats an ISO 8601 string into a value to be set in the form control.parseToString(formattedValue, state, element): Function used to parse the form control value into an ISO 8601 string to be set in the form manager.
<CurrentPath>¶
The <CurrentPath> component makes the current base path for its children:
<CurrentPath path="/tripDates">
<TextInput path="departure" /> {/* Resolved path is "/tripDates/departure" */}
<TextInput path="return" /> {/* Resolved path is "/tripDates/return" */}
</CurrentPath>
Children of <CurrentPath> that use KForm-React hooks or components taking paths, will have their
path resolved against the provided path.
useCurrentPath¶
The useCurrentPath hook provides access to the path currently in scope.
The path currently in scope can be specified via the <CurrentPath> component.
useResolvedPath¶
The useResolvedPath hook resolves the provided path against the path currently in scope:
The returned path instance is stable across renders, so it can be safely used in dependency arrays of React hooks.
useIssuesTracker¶
The useIssuesTracker hook can be used to keep track of all validation issues with paths matching
the provided path:
The path defaults to ./** (all descendants of the current path). Supported options are:
formManager: The form manager instance to use. Defaults to the one in scope.enabled: Whether to enable validation issues tracking.-
issuesOrderCompareFn(path1, path2): Function which may be provided to specify the order of the resulting issues.It receives the paths of the issues being compared and should return a number in typical Array.prototype.sort#compareFn fashion to determine the order of the issues.
The default ordering is based on the order in which fields were declared within the form schema. If this function is provided, and
0is returned, then the default schema-based ordering will be used for disambiguation. Note that this function should not change on every render, as any change to this function will cause the tracker to “restart”.
The hook returns an object with the following properties:
initialized: Whether the tracker has been initialised.info: Array containing validation information about the values with issues. Each element is an object with the following properties:path: Path of the value with issues.issues: Issues of the value (the array is guaranteed to not be empty).localDisplayStatus: Local display status of the value (ignoring issues of descendants).
errors: Total number of errors.warnings: Total number of warnings.
useSubscription¶
The useSubscription allows one to subscribe to all form manager events with paths matching the
provided path and to run the provided callback for each event:
The following options are supported:
formManager: The form manager instance to use. Defaults to the one in scope.enabled: Whether to enable the subscription.onSubscribe(): Function called after the subscription completes, but before any event is emitted.onUnsubscribe(): Function called after unsubscribing.
Changes to any of the callbacks do not cause resubscriptions.
useSubmitting¶
The useSubmitting hook returns whether the form is currently being submitted.
useResetting¶
The useResetting hook returns whether the form is currently being reset.
useAutoValidationStatus¶
The useAutoValidationStatus hook returns the current status of the automatic validation daemon.
The automatic validation status can be one of:
inactive: Automatic validations are inactive (validationModeis set to"manual").activeIdle: Automatic validations are active but idle (validationModeis set to"auto"but there is nothing to validate).activeRunning: Automatic validations are active and running (validationModeis set to"auto"and values are currently being validated).
setKFormLogLevel¶
The setKFormLogLevel can be used to aid debugging by setting the log level used by the core KForm
library.
The log level can be set to one of: "trace", "debug", "info", "warn", "error", and
"off". The default level is "info".
During development (i.e. whenever process.env.NODE_ENV is not set to "production"), the
setKFormLogLevel function is available as a global variable. This allows developers to dynamically
change the log level by opening the browser’s console and typing, for example,
setKFormLogLevel("debug"). Upon doing so, log messages should start appearing in the console as
one interacts with the form.