防止提交路线更改 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
来自哪里,但逻辑就是这样。
你应该检查它是否已经提交过一次,如果已经提交,则跳过它,仅在第二次提交时提交。
如果你提供一个工作示例,我可以测试它,如果上面的代码不起作用,我可以让它工作。
【讨论】:
我重新创建了工作示例,但看起来<AutoSave/>
在那里工作不正确 - codesandbox.io/s/quirky-bouman-7h663
@Arthur 您的示例还有其他与AutoSave
无关的问题。
已修复,你现在可以检查一下,顺便我用的是旧版的自动保存
Formik 在 props formik.submitCount 中保留表单提交的计数,您不需要单独设置状态以上是关于防止提交路线更改 Formik AutoSave的主要内容,如果未能解决你的问题,请参考以下文章