防止在路线更改时提交 Formik AutoSave
Prevent submit on route change Formik AutoSave
我的应用程序包含 <AutoSave/>
组件。一旦表单值更改,该组件就会调用提交。一切正常,但在更改路线时,它会更改表单值并 <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..."}</>
}
我的应用程序:
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>
)
您需要像 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
来自哪里,但逻辑就是这样。
你应该检查它是否已经提交过一次,如果是,则跳过它,只在第二次提交时提交。
如果你提供一个工作示例,我可以测试它,如果上面的代码不起作用,我可以让它工作。
查看: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)}
>
我的应用程序包含 <AutoSave/>
组件。一旦表单值更改,该组件就会调用提交。一切正常,但在更改路线时,它会更改表单值并 <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..."}</>
}
我的应用程序:
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>
)
您需要像 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
来自哪里,但逻辑就是这样。
你应该检查它是否已经提交过一次,如果是,则跳过它,只在第二次提交时提交。
如果你提供一个工作示例,我可以测试它,如果上面的代码不起作用,我可以让它工作。
查看: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)}
>