Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 640
Jason Avatar asked Jul 02 '20 00:07

Jason


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 {useFormState} from 'react-final-form'

const EditFormAutoSave = ({waitInterval = 1000}) => {
  const {dirty, valid, values} = useFormState({subscription: {
    dirty: true, valid: true, values: true,
  }})
  const {save, saving} = useEditContext()
  const shouldSaveRef = useRef()
  const saveDebouncedRef = useRef()
  /*
   * Determine whether 'save' 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(() => {
    shouldSaveRef.current = dirty && valid && !saving
  }, [dirty, saving, valid])
  /*
   * Debounce the 'save()' 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(() => {
    saveDebouncedRef.current = _.debounce(save, waitInterval)
  }, [save, waitInterval])
  /*
   * Whenever the form data got dirty, schedule saving data
   */
  useEffect(() => {
    if (shouldSaveRef.current) {
      saveDebouncedRef.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(() => () => {
    shouldSaveRef.current && saveDebouncedRef.current.flush()
  }, [])
}

export default EditFormAutoSave
like image 127
Lars Blumberg Avatar answered Jan 02 '23 11:01

Lars Blumberg