防止在路由更改时提交 Formik AutoSave

Art*_*hur 5 javascript reactjs next.js formik react-hooks

我的应用程序具有<AutoSave/>组件形式。一旦表单值改变,这个组件就会调用 submit 。一切正常,但是当更改路由时,它会更改表单值并<AutoSave/>调用提交。如何解决这个问题呢?一个可能的解决方案是<AutoSave/>在更改路由时再次挂载。

代码沙盒

自动保存:

import React, { useEffect, useCallback } from 'react'
import { useFormikContext } from 'formik'
import debounce from 'lodash.debounce'

const AutoSave = ({ debounceMs }) => {
  const formik = useFormikContext()

  const debouncedSubmit = useCallback(
    debounce(formik.submitForm, debounceMs),
    [formik.submitForm, debounceMs]
  )

  useEffect(() => debouncedSubmit, [debouncedSubmit, formik.values])

  return <>{!!formik.isSubmitting && "saving..."}</>
}
Run Code Online (Sandbox Code Playgroud)

我的应用程序:

const App: FC = () => {
  const {books} = getBooks() // [{id: 1, title: 'test', summary: 'test'}, ...]
  const {query} = useRouter()

  const handleSubmit = useCallback(async values => {
    try {
      await API.patch('/books', {id: query.book, ...values})
    } catch (e) {}
  }, [query.book])

  return (
    <>
      <span>Books</span>
      {books.map(({id, title}, key) => (
        <Link key={key} href='/book/[book]' as={`/book/${id}`}>
          <a>{title}</a>
        </Link>
      ))}
      {query.book && (
        <MainForm  
         book={books.find(book => book.id === query.book)}
         handleSubmit={handleSubmit}/>
      )}
    </>
  )
}
Run Code Online (Sandbox Code Playgroud)

主窗体:

type Props = {
  book: BookProps // {id: string, title: string ...},
  handleSubmit: (values) => Promise<void>
}

const MainForm: FC<Props> = ({book, handleSubmit}) => (
  <Formik 
    enableReinitialize 
    initialValues={{title: book.title, summary: book.summary}}
    handleSubmit={values => handleSubmit(values)}>
    {() => (
      <Form>
        //...My fields...
        <AutoSave debounceMs={500}/> // <=== AutoSave with debounce
      </Form>
    )}
  </Formik>
)
Run Code Online (Sandbox Code Playgroud)

Nin*_*ham 5

看看:https : //codesandbox.io/s/clever-sun-057vy

# Problem

useEffect(() => debouncedSubmit, [debouncedSubmit, formik.values]);
Run Code Online (Sandbox Code Playgroud)

formik.values即使组件安装,也将始终更改。这就是为什么debouncedSubmit在路由更改时被调用的。

所以基本上,我们不想将它作为组件首次渲染运行,而是在用户更改表单时运行。

formik.dirty是关键。formik.dirty在提交之前检查一下。

const AutoSave = ({ debounceMs }) => {
  const formik = useFormikContext();

  const debouncedSubmit = useCallback(
    debounce(formik.submitForm, debounceMs),
    [formik.submitForm, debounceMs]
  );

  useEffect(() => {
    formik.dirty && debouncedSubmit();
  }, [debouncedSubmit, formik.dirty, formik.values]);

  return <>{!!formik.isSubmitting && 'saving...'}</>;
};
Run Code Online (Sandbox Code Playgroud)

另一件事是 Formik 实例。这Formik将用于所有书籍。因此,在将新书绑定到表单中时,您需要使用enableReinitialize prop重置表单。

<Formik
  enableReinitialize
  initialValues={{ title: book.title, summary: book.summary, id: book.id }}
  onSubmit={values => handleSubmit(values)}
>
Run Code Online (Sandbox Code Playgroud)

或者为每本书使用单独的实例 key={book.id}

<Formik
  key={book.id}
  initialValues={{ title: book.title, summary: book.summary, id: book.id }}
  onSubmit={values => handleSubmit(values)}
>
Run Code Online (Sandbox Code Playgroud)