首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何使用React Hook-Form自动保存进行回退查询

如何使用React Hook-Form自动保存进行回退查询
EN

Stack Overflow用户
提问于 2022-09-05 21:54:55
回答 1查看 509关注 0票数 1

我试图使用React Hook- form和React Query创建一个表单,每当用户更改任何字段(取消)时,都会自动生成该表单。我正在接近,但当我变异时,它会产生一个无限的循环。以下是我所拥有的:

代码语言:javascript
复制
"@tanstack/react-query": "^4.2.3",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-hook-form": "^7.34.2",
代码语言:javascript
复制
import React from 'react'
import { useMutation, useQuery } from '@tanstack/react-query'
import { useForm } from 'react-hook-form'
import { yupResolver } from '@hookform/resolvers/yup'
import * as Yup from 'yup'
import debounce from 'just-debounce-it'

type NameType = {
    id: number
    firstName: string
    lastName: string
}

const schemaValidation = Yup.object().shape({
    id: Yup.number().required('Required'),
    firstName: Yup.string().required('Required'),
    lastName: Yup.string()
        .min(2, 'Must be greater than 1 character')
        .max(50, 'Must be less than 50 characters')
})

const getMockData = async () => {
    const name: NameType = {
        id: 1,
        firstName: 'John',
        lastName: 'Doe'
    }
    return await Promise.resolve(name)
}

const saveChangeToDatabase = async (args: NameType) => {
    console.count('payload for patch:' + JSON.stringify(args))
    return await Promise.resolve(args)
}

const NameForm = () => {
    const queryResult = useQuery(['user'], getMockData)
    const mutationResult = useMutation(saveChangeToDatabase, {
        onSuccess: (nameToSave: NameType) => {
            console.count('success mutating: ' + JSON.stringify(nameToSave))
        }
    })

    const {
        register,
        reset,
        watch,
        formState: { isValid, isDirty, errors }
    } = useForm<NameType>({
        mode: 'all',
        criteriaMode: 'all',
        resolver: yupResolver(schemaValidation)
    })
    const fieldData = watch()

    const handleDebouncedChange = debounce((data: NameType) => {
        mutationResult.mutateAsync(data)
    }, 500)

    React.useEffect(() => {
        reset(queryResult.data)
    }, [queryResult.data])

    React.useEffect(() => {
        if (isValid && isDirty) {
            handleDebouncedChange(fieldData)
        }
    }, [fieldData, isValid, isDirty])

    if (queryResult.isLoading) {
        return <h2>Loading...</h2>
    }

    return (
        <div
            style={{
                display: 'flex',
                flexDirection: 'column',
                margin: 'auto',
                width: 300
            }}>
            <input {...register('firstName')} placeholder='First name' />
            <div style={{ color: 'red' }}>{errors && errors?.firstName?.message}</div>
            <input {...register('lastName')} placeholder='Last name' />
            <div style={{ color: 'red' }}>{errors && errors?.lastName?.message}</div>
            {'Field data: ' + JSON.stringify(fieldData)}
        </div>
    )
}

export default NameForm

我还在这里复制了一个创建-反应-应用程序。您可以克隆回购,运行npm,npm,当您更改表单时会看到问题。这是您唯一需要查看的页面:

代码语言:javascript
复制
https://github.com/k-38/react-query_react-hook-form_autosave/blob/main/src/NameForm.tsx

任何帮助都很感谢,谢谢。

更新:非常感谢你的回答。我接受了您的答案,并在一个自定义钩子中抽象出了许多退出/回调逻辑(尚未输入):

代码语言:javascript
复制
import React from 'react'
import debounce from 'just-debounce-it'

export function useDebouncedAutoSave({
    mutationResult,
    validationSchema,
    getValues,
    reset,
    queryResult
}: {
    mutationResult: any
    validationSchema: any
    getValues: any
    reset: any
    queryResult: any
}) {
    React.useEffect(() => {
        reset(queryResult.data)
    }, [queryResult.data])

    const handleDebouncedChange = React.useMemo(
        () =>
            debounce((data: any) => {
                mutationResult.mutateAsync(data)
            }, 500),
        [mutationResult.mutateAsync]
    )

    const onChange = async () => {
        try {
            const values = getValues()
            const validated = await validationSchema.validate(values)
            if (!validated) return
            handleDebouncedChange(validated)
        } catch (e) {}
    }

    return {
        onChange
    }
}
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-09-05 22:44:06

因此,在带有useEffect的功能组件中,无限循环通常是由于每个“循环周期”上的依赖值发生了变化。

文档中,我们可以读到:

对呈现阶段而不是useEffect的deps进行了优化,要检测值更新,您可能需要使用外部自定义挂钩进行值比较。

我怀疑(没有时间看代码) watch返回值是在每次呈现时创建的。那么每个呈现上的fieldData都是对一个新对象的引用。

大多数情况下,我依赖于onChangeonBlur表单事件。

代码语言:javascript
复制
function Form() {
  const onChangeHandler = () => { /* ... */ };
  return <form onChange={onChangeHandler}>
   {/* ... */}
  </form>
}

然后我使用useForm().getValues函数来检索当前的表单值,但是您需要使用模式验证来在"autosave“函数有效时触发它。

另一种解决方案(也许是更简单的解决方案)是使用一个自定义钩子来深入比较这些值。您可以从useDeepCompareEffect反应-使用中了解到这一点。

您的代码中还有另一个错误,即使用const debouncedFunction = debounce(myFunction, 500)不起作用。一个“脱节”的功能是回忆录。由于我们处于呈现函数(functional )中,回忆录函数将在每次呈现时创建,因此它将被调用,而不考虑您设置的阈值。

为此,您需要使用React.useMemo

代码语言:javascript
复制
  const { mutateAsync } = mutationResult;
  const handleDebouncedChange = React.useMemo(
    () =>
      debounce((data: NameType) => {
        mutateAsync(data);
      }, 500),
    [mutateAsync]
  );

一个完全工作的版本可在此作为代码框提供将是:

代码语言:javascript
复制
import React from "react";
import { useMutation, useQuery } from "@tanstack/react-query";
import { useForm } from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup";
import * as Yup from "yup";
import debounce from "just-debounce-it";

type NameType = {
  id: number;
  firstName: string;
  lastName: string;
};

const schemaValidation = Yup.object().shape({
  id: Yup.number().required("Required"),
  firstName: Yup.string().required("Required"),
  lastName: Yup.string()
    .required()
    .min(2, "Must be greater than 1 character")
    .max(50, "Must be less than 30 characters")
});

const getMockData = async () => {
  const name: NameType = {
    id: 1,
    firstName: "John",
    lastName: "Doe"
  };
  return await Promise.resolve(name);
};

const saveChangeToDatabase = async (args: NameType) => {
  console.count("payload for patch:" + JSON.stringify(args));
  return await Promise.resolve(args);
};

const NameForm = () => {
  const queryResult = useQuery(["user"], getMockData);
  const mutationResult = useMutation(saveChangeToDatabase, {
    onSuccess: (nameToSave: NameType) => {
      console.count("success mutating: " + JSON.stringify(nameToSave));
    }
  });

  const { register, reset, watch, getValues, formState } = useForm<NameType>({
    mode: "all",
    criteriaMode: "all",
    resolver: yupResolver(schemaValidation)
  });
  const { errors } = formState;
  const fieldData = watch();

  const { mutateAsync } = mutationResult;
  const handleDebouncedChange = React.useMemo(
    () =>
      debounce((data: NameType) => {
        mutateAsync(data);
      }, 500),
    [mutateAsync]
  );

  React.useEffect(() => {
    reset(queryResult.data);
  }, [queryResult.data]);

  const onChange = async () => {
    const data = getValues();
    try {
      console.log(formState);
      const validated = await schemaValidation.validate(data);
      handleDebouncedChange(validated);
    } catch (e) {}
  };

  if (queryResult.isLoading) {
    return <h2>Loading...</h2>;
  }

  return (
    <form
      style={{
        display: "flex",
        flexDirection: "column",
        margin: "auto",
        width: 300
      }}
      onChange={onChange}
    >
      <input {...register("firstName")} placeholder="First name" />
      <div style={{ color: "red" }}>{errors && errors?.firstName?.message}</div>
      <input {...register("lastName")} placeholder="Last name" />
      <div style={{ color: "red" }}>{errors && errors?.lastName?.message}</div>
      {"Field data: " + JSON.stringify(fieldData)}
    </form>
  );
};

export default NameForm;
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/73614962

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档