Import
import { Form } from '@dnb/eufemia/extensions/forms'// Use Form.useSnapshot
Description
The Form.useSnapshot hook lets you store data snapshots of your form data, either inside or outside of the form context.
import { Form } from '@dnb/eufemia/extensions/forms'function MyComponent() {const { createSnapshot, applySnapshot, revertSnapshot } =Form.useSnapshot()return <>MyComponent</>}render(<Form.Handler><MyComponent /></Form.Handler>,)
The hook returns an object with the following properties:
createSnapshotwill store the current data as a new snapshot with the given id.applySnapshotwill revert the data to the snapshot with the given id (required).revertSnapshotwill revert the data to the snapshot with the given id (required). A reverted snapshot gets deleted from the memory.
Partial data snapshots
In order to create and revert a snapshot for a specific part of the data context, you can use the Form.Snapshot component:
import { Form, Field } from '@dnb/eufemia/extensions/forms'function MyForm() {return (<Form.Handler><Form.Snapshot name="my-snapshot-slice-name"><Field.String path="/foo" label="Will be reverted" /><Field.String path="/bar" label="Me too" /></Form.Snapshot><Field.String path="/baz" label="Will stay as before" /></Form.Handler>)}
When calling the createSnapshot or revertSnapshot functions, you can pass in your snapshot name (my-snapshot-slice-name) as the second parameter. This will make sure that the snapshot is only applied to the given part of the form data.
createSnapshot('my-snapshot-1', 'my-snapshot-slice-name')revertSnapshot('my-snapshot-1', 'my-snapshot-slice-name')
You can check out examples in the demo section.
Usage of the Form.useSnapshot hook
You can use the Form.useSnapshot 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.
Without an id property
Here "Component" is rendered inside the Form.Handler component and does not need an id property to access the snapshot:
import { Form } from '@dnb/eufemia/extensions/forms'function MyForm() {return (<Form.Handler><Component /></Form.Handler>)}function Component() {const { createSnapshot, revertSnapshot } = Form.useSnapshot()}
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 { createSnapshot, revertSnapshot } = Form.useSnapshot(myFormId)}
This is beneficial when you need to utilize the form data in other places within your application.
Demos
Undo / Redo
const MyComponent = () => { const { createSnapshot, applySnapshot } = Form.useSnapshot() const pointerRef = React.useRef(0) React.useEffect(() => { createSnapshot(pointerRef.current, 'my-snapshot-slice') }, [createSnapshot]) const changeHandler = React.useCallback(() => { pointerRef.current += 1 createSnapshot(pointerRef.current, 'my-snapshot-slice') }, [createSnapshot]) const undoHandler = React.useCallback(() => { pointerRef.current -= 1 applySnapshot(pointerRef.current, 'my-snapshot-slice') }, [applySnapshot]) const redoHandler = React.useCallback(() => { pointerRef.current += 1 applySnapshot(pointerRef.current, 'my-snapshot-slice') }, [applySnapshot]) return ( <> <Form.Card> <Form.Snapshot name="my-snapshot-slice"> <Field.String path="/foo" label="Will be reverted" onChange={changeHandler} /> </Form.Snapshot> <Field.String path="/bar" label="Will stay" /> </Form.Card> <Form.ButtonRow> <Button variant="secondary" onClick={undoHandler}> Undo </Button> <Button variant="secondary" onClick={redoHandler}> Redo </Button> </Form.ButtonRow> <Tools.Log top /> </> ) } render( <Form.Handler> <MyComponent /> </Form.Handler>, )
Used in a Wizard
This example reverts the form data to its previous state when the user navigates back to a previous step.
const MyForm = () => { const { createSnapshot, revertSnapshot } = Form.useSnapshot('my-form') return ( <Form.Handler id="my-form"> <Wizard.Container onStepChange={(index, mode, args) => { if (mode === 'previous') { revertSnapshot(String(args.id), 'my-snapshot-slice') } else { createSnapshot(args.previousStep.id, 'my-snapshot-slice') } }} > <Wizard.Step title="Step A" id="step-a"> <Form.Snapshot name="my-snapshot-slice"> <Field.String path="/foo" label="Will be reverted" /> </Form.Snapshot> <Field.String path="/bar" label="Will stay" /> <Wizard.Buttons /> </Wizard.Step> <Wizard.Step title="Step B" id="step-b"> <Field.String path="/foo" label="Will be reverted" /> <Field.String path="/bar" label="Will stay" /> <Wizard.Buttons /> </Wizard.Step> </Wizard.Container> </Form.Handler> ) } render(<MyForm />)