React-hook-form 和 useState (切换)

Mat*_*sen 3 reactjs next.js faunadb react-hook-form use-state

我有以下用例:

\n

用户想要切换配置文件是否处于活动状态。

\n

配置: \nNext.js、\nFauna DB、\nreact-hook-form

\n

我使用 useState 来更改切换上的状态,并使用 React-hook-forms 将其他值发送到我的 Fauna 数据库以及切换中的状态。我希望切换具有数据库中的状态,当用户切换它并按下提交按钮时,我想更改数据库中的状态。

\n

当我切换数据库时,我似乎无法将正确的状态发送回数据库。

\n

主要成分:

\n
export default function Component() {\n const [status, setStatus] = useState(\n    userData?.profileStatus ? userData.profileStatus : false\n  );\n\nconst defaultValues = {\n    profileStatus: status ? userData?.profileStatus : false\n  };\n\nconst { register, handleSubmit } = useForm({ defaultValues });\n\n  const handleUpdateUser = async (data) => {\n\n    const {\n      profileStatus\n    } = data;\n    try {\n      await fetch(\'/api/updateProfile\', {\n        method: \'PUT\',\n        body: JSON.stringify({\n          profileStatus\n        }),\n        headers: {\n          \'Content-Type\': \'application/json\'\n        }\n      });\n      alert(`submitted data: ${JSON.stringify(data)}`);\n    } catch (err) {\n      console.error(err);\n    }\n  };\n\nreturn (\n  <div>\n    <form onSubmit={handleSubmit(handleUpdateUser)}>\n      <Toggle status={status} setStatus={setStatus} />\n      <button type="submit">\n        Save\n      </button>\n    </form>\n  </div>\n )\n}\n
Run Code Online (Sandbox Code Playgroud)\n

切换组件:

\n
import { Switch } from \'@headlessui/react\';\n\nfunction classNames(...classes) {\n  return classes.filter(Boolean).join(\' \');\n}\n\nexport default function Toggle({status , setStatus}) {\n\n  return (\n    <Switch.Group as="div" className="flex items-center justify-between">\n      <span className="flex-grow flex flex-col">\n        <Switch.Label\n          as="span"\n          className="text-sm font-medium text-gray-900"\n          passive\n        >\n          Profilstatus\n        </Switch.Label>\n        <Switch.Description as="span" className="text-sm text-gray-500 w-44">\n          Her s\xc3\xa6tter du om din profil skal v\xc3\xa6re aktiv eller inaktiv.\n        </Switch.Description>\n      </span>\n      <Switch\n        checked={status}\n        onChange={setStatus}\n        className={classNames(\n          status ? \'bg-blue-600\' : \'bg-gray-200\',\n          \'relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500\'\n        )}\n      >\n        <span\n          aria-hidden="true"\n          className={classNames(\n            status ? \'translate-x-5\' : \'translate-x-0\',\n            \'pointer-events-none inline-block h-5 w-5 rounded-full bg-white shadow transform ring-0 transition ease-in-out duration-200\'\n          )}\n        />\n      </Switch>\n    </Switch.Group>\n  );\n}\n
Run Code Online (Sandbox Code Playgroud)\n

updateProfile.js

\n
import { updateProfileInfo } from \'@/utils/Fauna\';\nimport { getSession } from \'next-auth/react\';\n\nexport default async (req, res) => {\n  const session = await getSession({ req });\n  if (!session) return res.status(401);\n\n  const userId = session.user.id;\n  if (req.method !== \'PUT\') {\n    return res.status(405).json({ msg: \'Method not allowed\' });\n  }\n\n  const {\n    profileStatus,\n    image,\n    about,\n    preferences,\n    socialmedia\n   } = req.body;\n  try {\n    const updated = await updateProfileInfo(\n      userId,\n      profileStatus,\n      image,\n      about,\n      preferences,\n      socialmedia\n    );\n    return res.status(200).json(updated);\n  } catch (err) {\n    console.error(err);\n    res.status(500).json({ msg: \'Something went wrong.\' });\n  }\n  res.end();\n};\n
Run Code Online (Sandbox Code Playgroud)\n

动物群.js

\n
const updateProfileInfo = async (\n  userId,\n  profileStatus,\n  image,\n  about,\n  preferences,\n  socialmedia\n) => {\n  return await faunaClient.query(\n    q.Update(q.Ref(q.Collection(\'users\'), userId), {\n      data: {\n        profileStatus,\n        image,\n        about,\n        preferences,\n        socialmedia\n      }\n    })\n  );\n};\n\nmodule.exports = {\n  updateProfileInfo\n\n}\n
Run Code Online (Sandbox Code Playgroud)\n

你们能看出我做错了什么吗?

\n

kno*_*fel 15

我制作了一个小沙箱来演示如何使用react-hook-form.

\n

它不起作用的原因是,react-hook-form切换开关时您永远不会更新内部状态,您只更新您的useState. 因此,当您调用handleUpdateUser作为参数传递的数据时,它是您通过设置的初始数据defaultValues

\n

实际上这里不需要使用useState,因为你可以只使用react-hook-form\ 的内部表单状态。为此,您必须使用 提供的<Controller />组件react-hook-form,因为<Switch />Headless UI 中的组件@headlessui/react是外部受控组件,它不会公开ref实际<input />元素的 prop(<Switch />使用 a<button />而不是<input />元素)。您可以在这里找到更多信息。

\n

通过这种方式,您还可以通过提供 a and属性而不是and来使您的<Toggle />重用更加通用。但当然您也可以仍然使用这些名称。它将在我散布在组件上的对象上提供一个和支柱。valueonChangestatussetStatus<Controller />valueonChangefield<Toggle />

\n

在您的示例中,尚不清楚您的<Component />组件将如何接收初始userData. 我假设您会发出 api 请求,因此我将其放入useEffect. 要在 api 调用完成后更新表单状态,您必须使用提供的reset方法react-hook-form。如果仅在已加载<Component />时进行渲染userData,则可以省略此步骤并将结果传递给defaultValuesto useForm

\n

我用一个简单的 Promise 模拟了 api 调用,但您应该明白了。

\n

组件.js

\n
import { useEffect } from "react";\nimport { Controller, useForm } from "react-hook-form";\nimport Toggle from "./Toggle";\n\n// Server Mock\nlet databaseState = {\n  profileStatus: true\n};\n\nconst getUserData = () => Promise.resolve(databaseState);\nconst updateUserData = (newState) => {\n  databaseState = newState;\n\n  return Promise.resolve(newState);\n};\n\nfunction Component() {\n  const { control, reset, handleSubmit } = useForm({\n    defaultValues: { profileStatus: false }\n  });\n\n  useEffect(() => {\n    const loadData = async () => {\n      const result = await getUserData();\n\n      reset(result);\n    };\n\n    loadData();\n  }, [reset]);\n\n  const handleUpdateUser = async (data) => {\n    try {\n      const result = await updateUserData(data);\n\n      console.log(result);\n    } catch (err) {\n      console.error(err);\n    }\n  };\n\n  return (\n    <div>\n      <form onSubmit={handleSubmit(handleUpdateUser)}>\n        <Controller\n          control={control}\n          name="profileStatus"\n          render={({ field: { ref, ...field } }) => <Toggle {...field} />}\n        />\n        <button type="submit">Save</button>\n      </form>\n    </div>\n  );\n}\n
Run Code Online (Sandbox Code Playgroud)\n

切换.js

\n
import { Switch } from "@headlessui/react";\n\nfunction classNames(...classes) {\n  return classes.filter(Boolean).join(" ");\n}\n\nexport default function Toggle({ value, onChange }) {\n  return (\n    <Switch.Group as="div" className="flex items-center justify-between">\n      <span className="flex-grow flex flex-col">\n        <Switch.Label\n          as="span"\n          className="text-sm font-medium text-gray-900"\n          passive\n        >\n          Profilstatus\n        </Switch.Label>\n        <Switch.Description as="span" className="text-sm text-gray-500 w-44">\n          Her s\xc3\xa6tter du om din profil skal v\xc3\xa6re aktiv eller inaktiv.\n        </Switch.Description>\n      </span>\n      <Switch\n        checked={value}\n        onChange={onChange}\n        className={classNames(\n          value ? "bg-blue-600" : "bg-gray-200",\n          "relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"\n        )}\n      >\n        <span\n          aria-hidden="true"\n          className={classNames(\n            value ? "translate-x-5" : "translate-x-0",\n            "pointer-events-none inline-block h-5 w-5 rounded-full bg-white shadow transform ring-0 transition ease-in-out duration-200"\n          )}\n        />\n      </Switch>\n    </Switch.Group>\n  );\n}\n
Run Code Online (Sandbox Code Playgroud)\n

编辑鼓舞人心的月亮0lqhdg

\n