React-admin: How to implement a autosave/background-save feature?

Viewed 359

How to implement a autosave/background-save feature using react-admin?

I want a feature when I am editing a rich input field, such as a rich text, a background saving will push my text to server, without touch my focus and edit position.

I tried using EditController and SimpleFrom, this will re-render form and get recorder from server, and lost focus and editing position. Any example or advice on this please?

1 Answers

I've built a drop-in component called EditFormAutoSave which you can simply add to an existing EditForm for auto-save to happen. It saves all changed "dirty" fields after a short wait interval of inactivity as well es when the edit form gets unmounted to prevent any data loss.

Important: The edit form needs to have redirect={false} set to prevent redirect from occurring after a successful auto-save.

Usage

const YourResourceEdit = props => (
  <Edit mutationMode="optimistic" {...props}>
    <SimpleForm redirect={false} toolbar={<EditToolbar />}>
      <EditFormAutoSave waitInterval={2000} />
      <TextInput source="someFieldName" />
      ...
    </SimpleForm>
  </Edit>
)

EditFormAutoSave.js

import _ from 'lodash'
import {useEffect, useRef} from 'react'
import {useEditContext} from 'react-admin'
import {useForm, useFormState} from 'react-final-form'

const EditFormAutoSave = ({waitInterval = 1000}) => {
  const {dirty, valid, values} = useFormState({subscription: {
    dirty: true, valid: true, values: true,
  }})
  const {submit} = useForm()
  const {saving} = useEditContext()
  const shouldSubmitRef = useRef()
  const submitDebouncedRef = useRef()
  /*
   * Determine whether 'submit' should be called by any of the following effects. Use a
   * 'ref' instead of a 'state' so that the an unmount effect can be set up which musn't
   * have state dependencies.
   */
  useEffect(() => {
    shouldSubmitRef.current = dirty && valid && !saving
  }, [dirty, saving, valid])
  /*
   * Debounce the 'submit()' function and store it in a 'ref' for the same reason as
   * above (it needs to be called on unmount which musn't have state dependencies).
   */
  useEffect(() => {
    submitDebouncedRef.current = _.debounce(submit, waitInterval)
  }, [submit, waitInterval])
  /*
   * Whenever the form data got dirty, schedule saving data
   */
  useEffect(() => {
    if (shouldSubmitRef.current) {
      submitDebouncedRef.current({...values}, /* redirectTo= */false)
    }
  }, [dirty, saving, valid, values])
  /*
   * On component unmount submit any unsubmitted changes so that changed ("dirty") fields
   * get persisted. Said differently this effects prevents data loss of unsaved changes.
   */
  useEffect(() => () => {
    shouldSubmitRef.current && submitDebouncedRef.current.flush()
  }, [])
  return null
}

export default EditFormAutoSave
Related