防止提交路线更改 Formik AutoSave

Posted

技术标签:

【中文标题】防止提交路线更改 Formik AutoSave【英文标题】:Prevent submit on route change Formik AutoSave 【发布时间】:2020-04-04 16:33:25 【问题描述】:

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

Codesandbox

自动保存:

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..."</>

我的应用:

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/>
      )
    </>
  )

主窗体:

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>
)

【问题讨论】:

也许您可以尝试在您的 formik 道具上使用 enableReinitialize = true 获取表单值。由于您的组件顶部有状态,因此它会因新值而重新呈现。 @Lhew,嗨!我在表单中添加了enableReinitialize,但这不是解决这个问题的关键。也许我需要比较上一个和下一个查询book id,然后再次挂载 AutoSave 【参考方案1】:

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

# Problem

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

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...'</>;
;

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

<Formik
  enableReinitialize
  initialValues= title: book.title, summary: book.summary, id: book.id 
  onSubmit=values => handleSubmit(values)
>

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

<Formik
  key=book.id
  initialValues= title: book.title, summary: book.summary, id: book.id 
  onSubmit=values => handleSubmit(values)
>

【讨论】:

【参考方案2】:

您需要有类似firstSubmit 的东西,您可以在其中检查firstSubmit 是否已经发生,因此它只会在第二次提交时调用AutoSave(实际上发生了变化)。

const AutoSave = (debounceMs) => 
    const [firstSubmit, setFirstSubmit] = React.useState(false)
    const formik = useFormikContext();

    const debouncedSubmit = React.useCallback(
      debounce(firstSubmit ? formik.submitForm : () => setFirstSubmit(true), debounceMs),
      [debounceMs, formik.submitForm, firstSubmit, setFirstSubmit]
    );

    React.useEffect(debouncedSubmit , [debouncedSubmit, formik.values]);

    return <>!!formik.isSubmitting ? 'saving...' : null</>;

我不确定代码是否有效,我还没有测试过,因为我不确定debounce 来自哪里,但逻辑就是这样。

你应该检查它是否已经提交过一次,如果已经提交,则跳过它,仅在第二次提交时提交。

如果你提供一个工作示例,我可以测试它,如果上面的代码不起作用,我可以让它工作。

【讨论】:

我重新创建了工作示例,但看起来 &lt;AutoSave/&gt; 在那里工作不正确 - codesandbox.io/s/quirky-bouman-7h663 @Arthur 您的示例还有其他与AutoSave 无关的问题。 已修复,你现在可以检查一下,顺便我用的是旧版的自动保存 Formik 在 props formik.submitCount 中保留表单提交的计数,您不需要单独设置状态

以上是关于防止提交路线更改 Formik AutoSave的主要内容,如果未能解决你的问题,请参考以下文章

防止多步 Formik 表单在重新渲染上一步时重置表单控件输入

Formik 禁用提交 btn

Formik + Yup:如何在提交前立即验证表单?

表格提交后,Formik Field不会清算

在 formik 组件之外处理 formik 表单

React JS:Yup & Formik 错误消息在提交时显示多次