Import
import { Form } from '@dnb/eufemia/extensions/forms'// Use Form.useData
Description
With the Form.useData hook, you can manage your form data from nested components and outside the form context (Form.Handler).
The hook returns an object with the following properties:
import { Form } from '@dnb/eufemia/extensions/forms'function MyComponent() {const {getValue,update,remove,set,data,filterData,reduceToVisibleFields,} = Form.useData()return <>MyComponent</>}render(<Form.Handler><MyComponent /></Form.Handler>,)
getValuewill return the value of the given path.updatewill update the value of the given path.removewill remove the given path from the data context (fields will reapply their values afterwards).setwill set the whole dataset.datawill return the whole dataset (unvalidated).filterDatawill filter the data based on your own logic.reduceToVisibleFieldswill reduce the given data set to only contain the visible fields (mounted fields).
Usage
You can use the Form.useData hook with or without an id (string, function, object or React Context as the reference) property, which is optional and can be used to link the data to a specific Form.Handler component.
TypeScript support
You can define the TypeScript type structure for your form data. This will help you to get better code completion and type checking.
NB: Use type instead of interface for the type definition.
type MyData = { firstName: string }const MyComponent = () => {const { data } = Form.useData<MyData>()return data.firstName}
Without an id property
Here "Component" is rendered inside the Form.Handler component and does not need an id property to access the form data:
import { Form } from '@dnb/eufemia/extensions/forms'function MyForm() {return (<Form.Handler><Component /></Form.Handler>)}function Component() {const { data } = Form.useData()}
With an id property
While in this example, "Component" is outside the Form.Handler context, but linked together via the id (string, function, object or React Context as the reference) property:
import { Form } from '@dnb/eufemia/extensions/forms'const myFormId = 'unique-id' // or a function, object or React Context referencefunction MyForm() {return (<><Form.Handler id={myFormId}>...</Form.Handler><Component /></>)}function Component() {const { data } = Form.useData(myFormId)}
This is beneficial when you need to utilize the form data in other places within your application.
Select a single value
import { Form } from '@dnb/eufemia/extensions/forms'function MyComponent() {const { getValue } = Form.useData()const value = getValue('/foo')}
Update data
If you need to update the data, you can use the update method.
It takes a path (JSON Pointer) and a callback function. The callback function receives the existing value as the first argument, and the second argument is the path itself. The callback function must return the new value.
import { Form } from '@dnb/eufemia/extensions/forms'function Component() {const { update } = Form.useData()useEffect(() => {update('/foo', 'new value')// - or with a callback function to get the existing valueupdate('/foo', (existingValue) => existingValue + 'new value')}, [])}
Extend the whole data set
With the set method, you can extend the data set. Existing data paths will be overwritten.
import { Form, Field } from '@dnb/eufemia/extensions/forms'const myFormId = 'unique-id' // or a function, object or React Context referencefunction MyForm() {const { data, set } = Form.useData(myFormId)useEffect(() => {set({ foo: 'bar' })}, [])return (<Form.Handler id={myFormId}><Field.String path="/foo" /></Form.Handler>)}
Visible data
You can use the reduceToVisibleFields function to get only the data of visible (mounted) fields. Check out the example in the demo section.
import { Form } from '@dnb/eufemia/extensions/forms'function MyComponent() {const { data, reduceToVisibleFields } = Form.useData()// Use useEffect to ensure we get the latest dataReact.useEffect(() => {console.log(reduceToVisibleFields(data))}, [data])return <>MyComponent</>}render(<Form.Handler><MyComponent /></Form.Handler>,)
In addition, you can include or exclude paths by using the keepPaths and removePaths options.
reduceToVisibleFields(data, {keepPaths: ['/foo'],removePaths: ['/bar'],})
Filter data
You can use the filterData function to filter your data. Check out the example below.
You simply give it the same kind of filter as you would within the onSubmit event callback.
The callback function receives the following properties in an object:
pathThe path of the field.valueThe value of the field.displayValueThe displayed value of the field.labelThe label of the field.propsThe given field properties.errorThe error of the field. Isundefinedif there is no error.
The callback function should return a boolean or undefined. Return false to exclude an entry.
It returns the filtered form data.
Tip: Depending on your use case – and instead of disabled – you may rather use a data-* attribute on your field (e.g. data-exclude-field) to filter the field out of the data set.
const filterDataHandler = ({ path, value, data, props, error }) => {if (props['data-exclude-field']) {return false}}const myFormId = 'unique-id' // or a function, object or React Context referenceconst MyForm = () => {const { filterData } = Form.useData(myFormId)const filteredData = filterData(filterDataHandler)return (<Form.Handler id={myFormId}><Field.String path="/foo" data-exclude-field /></Form.Handler>)}
const filterDataHandler = ({ path, value, data, props, error }) => {return !(error instanceof Error)}
Initial data
You decide where and when you want to provide the initial data to the form. It can be done via the Form.Handler component, or via the Form.useData Hook or Form.setData method – or even in each Field, with the value property.
import { Form, Field } from '@dnb/eufemia/extensions/forms'const myFormId = 'unique-id' // or a function, object or React Context referenceconst initialData = { foo: 'bar' }function MyForm() {return (<Form.Handler id={myFormId} data={initialData}><Field.String path="/foo" /></Form.Handler>)}function ComponentA() {Form.useData(myFormId, { foo: 'bar' })}function ComponentB() {const { set } = Form.useData(myFormId)useEffect(() => {set({ foo: 'bar' })}, [])}
Validation
tl;dr: the useData hook returns unvalidated data.
When you use an async onChange, onChangeValidator or onBlurValidator event handler on a field, it will delay the "submitted" value, because of its async nature.
That means, if you want to access the value of a field immediately, you can use the useData hook for that, as it always returns unvalidated data, in sync.