简体   繁体   English

Prisma、graphql 和反应钩子 forms:如何在更新表单突变中使用创建的值

[英]Prisma, graphql and react hook forms: how to use created values in update form mutation

I am trying to make a form that has to create and update functionality.我正在尝试制作一个必须创建和更新功能的表单。 At the moment, when I get to the update form, I can enter new values and they do save to psql, but the form loads with blank fields instead of the values used to create the existing object. I can't find the settings I need to apply to use the existing object values in the update form so that it loads with the fields displaying the currently saved values.目前,当我进入更新表单时,我可以输入新值并将它们保存到 psql,但该表单加载的是空白字段,而不是用于创建现有 object 的值。我找不到我的设置需要申请在更新表单中使用现有的 object 值,以便它加载显示当前保存值的字段。

My form has:我的表格有:

import * as React from "react"
import {  CgTrash, CgEditMarkup } from "react-icons/cg"
import { VscIssues } from "react-icons/vsc"

import { gql } from "@apollo/client"
import {
  Box,
  Button,
  ButtonGroup,
  IconButton,
  useDisclosure,
  Wrap,
  Stack,
  Table,
  Tbody,
  Tr,
  Td,
  TableContainer,
  Text
} from "@chakra-ui/react"
import Head from "next/head"
import { useToast } from "lib/hooks/useToast"
import { useForm } from "lib/hooks/useForm"
import { Form } from "components/Form"
import { FormError } from "components/FormError"
import { Input } from "components/Input"
import { Textarea } from "components/Textarea"

import { 
    // IssueGroupInput, 
    useAllIssueGroupsQuery, 
    useCreateIssueGroupMutation, 
    useUpdateIssueGroupMutation,
    useDeleteIssueGroupMutation,  
    IssueGroup as IssueGroupGQLType,  
}  from "lib/graphql"
//
// import { AdminCreateIssueGroupForm } from "components/AdminCreateIssueGroupForm"
// import { AdminUpdateIssueGroupForm } from "components/AdminUpdateIssueGroupForm"
import { AdminLayout } from "components/AdminLayout"
import { AuthedHomeLayout } from "components/AuthedHomeLayout"
import { Modal } from "components/Modal"
import Yup from "lib/yup"


const __ = gql`

  mutation CreateIssueGroup($data: IssueGroupInput!) {
    createIssueGroup(data: $data) {
      id
      title
      
    }
  }  
  query AllIssueGroups {
    allIssueGroups {
      id
      title
    }
  }

  mutation updateIssueGroup($id: String!) {
    updateIssueGroup(id: $id) {
        id
        title
       
    }
  }

  mutation deleteIssueGroup($id: String!) {
    deleteIssueGroup(id: $id) {
      id
      title

    }
  }
`

const IssueGroupSchema = Yup.object().shape({
    title: Yup.string().required(),
    description: Yup.string().required(),
    // issues: Yup.string()
  })
  

function IssueGroups() {
  const toast = useToast()
  // const [search, setSearch] = React.useState("")
  // const [selectedIssueGroups, setSelectedIssueGroups] = React.useState<string[]>([])
  const modalProps = useDisclosure()
  const modalPropsUpdate = useDisclosure()
//   // const [sort, setSort] = React.useState<Sort>({ createdAt: SortOrder.Desc })
  const [createIssueGroup] = useCreateIssueGroupMutation()
  const [updateIssueGroup] = useUpdateIssueGroupMutation()
  const [deleteIssueGroup] = useDeleteIssueGroupMutation()
  const {  data: issueGroups } = useAllIssueGroupsQuery()
  const defaultValues = {
    title: "",
    description: "",
    // issues: "",
  }
  const form = useForm({ defaultValues, schema: IssueGroupSchema })
  const [selectedIssueGroup, setSelectedIssueGroup ] = React.useState<Omit<
  IssueGroupGQLType,
  "createdAt" | "updatedAt" | "issues"
> | null>(null)
  
    const { isOpen, onOpen, onClose } = useDisclosure()

    const onCreateSubmit = async (data: Yup.InferType<typeof IssueGroupSchema>) => {
        return await form.handler(
            () =>
            createIssueGroup({ variables: { data } }),
            {
                onSuccess: async (res, toast) => {
                    // await refetchAllIssueGroups()
                    toast({
                        title: "IssueGroup created",
                        status: "success",
                    })
                    form.reset()
                    onClose()
                }
            }
        )
    }

    const handleUpdateSubmit = async (data: Yup.InferType<typeof IssueGroupSchema>) => {
        console.log(selectedIssueGroup.id)
        return await form.handler(
           
            () =>
           
            updateIssueGroup({ variables: { 
                id: selectedIssueGroup.id, 
                // issues: "none",
                data: { ...data } 
            } }),
        )
    }


    const onDeleteIssueGroup = async (id: string) => {
        return
        form.handler(
            () =>
                deleteIssueGroup({ variables: { id } }),
                {
                onSuccess: async () => {
                await fetch("api/issueGroups", { method: "delete"})
                toast({
                    title: "IssueGroup deleted",
                    status: "success",
                })
                },
                // refetchAllIssueGroups()
            },
        )
    }
  return (
    <Box>
      <Head>
        <title>trying to figure out how to crud in prisma react hook forms</title>
      </Head>

      <Wrap mb={4} spacing={2}>
        <Button
          onClick={modalProps.onOpen}
          leftIcon={<Box boxSize="18px" as={VscIssues} />}
          color="brand.white"
          fontWeight="normal"
          backgroundColor="brand.orange"
          _hover={{
            backgroundColor: "brand.green",
            color: "brand.white",
          }}
        >
          Create issueGroup
        </Button>
      </Wrap>

      <Modal {...modalProps} title="Create IssueGroup">
        {/* <AdminCreateIssueGroupForm onClose={modalProps.onClose} /> */}
        <Form {...form} onSubmit={onCreateSubmit}>
            <Stack>
                <Input name="title" label="Title" />

                {/* <Input name="description" label="Description" /> */}
                {/* <Text mb='8px' fontWeight="medium" fontSize="sm" > Description</Text> */}
                <Textarea name="description" label="Describe" rows={4} />

                <FormError />
                <ButtonGroup>
                <Button onClick={modalProps.onClose}>Cancel</Button>
                <Button
                    type="submit"
                    isLoading={form.formState.isSubmitting}
                    isDisabled={form.formState.isSubmitting}
                    color="brand.white"
                    fontWeight="normal"
                    backgroundColor="brand.orange"
                    _hover={{
                    backgroundColor: "brand.green",
                    color: "brand.white",
                    }}
                >
                    Create
                </Button>
                </ButtonGroup>
            </Stack>
            </Form>
      </Modal>

        <TableContainer maxW="80%">
            <Table variant='simple'>
            <Tbody>
            {issueGroups?.allIssueGroups.map((issueGroup) => (
                <Tr key={issueGroup.id}>

                <Td>
                    <Text textStyle="h6">{issueGroup.title}</Text>

                </Td>
                <Td>
                    <ButtonGroup>
                    <IconButton
                        variant='outline'
                        color="brand.blue"
                        fontWeight="normal"
                        backgroundColor="brand.white"
                        aria-label='Update Issue Group'
                        fontSize='15px'
                        ml={4}
                        icon={<CgEditMarkup />}
                        // onClick={modalPropsUpdate.onOpen}
                        onClick={() => {
                            setSelectedIssueGroup(issueGroup)
                            modalPropsUpdate.onOpen()
                        }}
                        _hover={{
                            backgroundColor: "brand.blue",
                            color: "brand.white",
                        }}
                    />

                    {/* {
                        selectedIssueGroup && isOpen && 
                        <Modal issueGroup={selectedIssueGroup} isOpen={isOpen} onClose={onClose}  title="Edit IssueGroup"  >
                    } */}

                        <Modal {...modalPropsUpdate } title="Edit IssueGroup"  >

                        {/* <AdminUpdateIssueGroupForm
                        onClose={modalPropsUpdate.onClose}
                        issueGroup={selectedIssueGroup} */}
                        {/* /> */}
                            <Form {...form} onSubmit={handleUpdateSubmit}>
                                <Stack>
                                    <Input name="title" label="Title" defaultValue={selectedIssueGroup.title} />

                                  
                                    <Textarea name="description" label="Describe" rows={4} defaultValue={selectedIssueGroup.description} />

// I also tried using the defaultValue as issueGroup.title/description, but those don't load the values either. // 我也尝试过将默认值用作 issueGroup.title/description,但它们也不会加载值。 When I try with issueGroup.title, the modal loads with empty fields (there are values saved in psql).当我尝试使用 issueGroup.title 时,模态加载了空字段(psql 中保存了值)。 When I try with selectedIssueGroup.title, the error message says:当我尝试使用 selectedIssueGroup.title 时,错误消息显示:

TypeError: Cannot read properties of null (reading 'title').类型错误:无法读取 null 的属性(读取“标题”)。

I know there is a title because it renders on the page when I list existing objects.我知道有一个标题,因为它会在我列出现有对象时呈现在页面上。 I also know the update is working because I can see psql update.我也知道更新正在运行,因为我可以看到 psql 更新。

                                    <FormError />
                                    <ButtonGroup>
                                    <Button onClick={modalPropsUpdate.onClose}>Cancel</Button>
                                    <Button
                                        type="submit"
                                        isLoading={form.formState.isSubmitting}
                                        isDisabled={form.formState.isSubmitting}
                                        color="brand.white"
                                        fontWeight="normal"
                                        backgroundColor="brand.orange"
                                        _hover={{
                                        backgroundColor: "brand.green",
                                        color: "brand.white",
                                        }}
                                    >
                                        Save changes
                                    </Button>
                                    </ButtonGroup>
                                </Stack>
                            </Form>
                        </Modal>

                    <IconButton
                        variant='outline'
                        color="brand.tomato"
                        fontWeight="normal"
                        backgroundColor="brand.white"
                        aria-label='Call Sage'
                        fontSize='15px'
                        ml={4}
                        icon={<CgTrash />}
                        onClick={() => onDeleteIssueGroup(issueGroup.id)}
                        _hover={{
                        backgroundColor: "brand.tomato",
                        color: "brand.white",
                        }}
                    />
                    </ButtonGroup>

                </Td>

                </Tr>
                ))}
            </Tbody>

            </Table>
        </TableContainer>

    </Box>
  )
}

IssueGroups.getLayout = (page: React.ReactNode) => <AuthedHomeLayout><AdminLayout>{page}</AdminLayout></AuthedHomeLayout>

export default IssueGroups

ARJUN'S SUGGESTIONS阿俊的建议

I tried each of Arjun's suggestions in his answer below.我在下面的回答中尝试了 Arjun 的每一个建议。 The results were as follows:结果如下:

First suggestion - being to add a use effect statement to the issues.tsx to try and set the value of issueGroup.第一个建议- 将使用效果语句添加到 issues.tsx 以尝试设置 issueGroup 的值。 To do this, I added this effect beneath the definition of setSelectedIssueGroup:为此,我在 setSelectedIssueGroup 的定义下方添加了此效果:

useEffect(()=>{ 
  if(selectedIssueGroup) 
    modalPropsUpdate.onOpen() 
}, [selectedIssueGroup]);

When I try this, I get an error in my vsCode that says:当我尝试这个时,我在 vsCode 中收到一条错误消息:

React Hook useEffect has a missing dependency: 'modalPropsUpdate'. React Hook useEffect 缺少依赖项:“modalPropsUpdate”。 Either include it or remove the dependency array.包括它或删除依赖项数组。

modalPropsUpdate is defined above that useEffect as: modalPropsUpdate 在 useEffect 上面被定义为:

  const modalPropsUpdate = useDisclosure()

To address that error, I tried commenting out that line.为了解决该错误,我尝试注释掉该行。 In that case, the useEffect is:在这种情况下,useEffect 是:

useEffect(()=>{ 
    if(selectedIssueGroup) 
      // modalPropsUpdate.onOpen() 
  }, [selectedIssueGroup]);

The error message says:错误消息说:

expression expected.预期的表达。

I don't know what this means.我不知道这是什么意思。

When I try to load the local host with this error, the error message is:当我尝试使用此错误加载本地主机时,错误消息是:

Error: x Unexpected token } .错误:x 意外的标记} Expected this, import, async, function, [ for array literal, { for object literal, @ for decorator, function, class, null, true, false, number, bigint, string, regexp, `预期这个,导入,异步,function,[对于数组文字,{对于 object 文字,@对于装饰器,function,class,null,真,假,数字,bigint,字符串,正则表达式,`

I think Arjun might be expecting that I would put async and await somewhere in this solution, but I don't know where to put them.我认为 Arjun 可能希望我将 async 和 await 放在这个解决方案的某个地方,但我不知道将它们放在哪里。

| | for template literal, (, or an identifier对于模板字面量、( 或标识符

The onClick handler for update still has:更新的 onClick 处理程序仍然具有:

 onClick={() => {
     setSelectedIssueGroup(issueGroup)
     modalPropsUpdate.onOpen()
                           
 }}

Second suggestion - being to use value instead of defaultValue in the input fields.第二个建议——在输入字段中使用值而不是默认值。

When I try this, by removing defaultValue and adding: value={issueGroup?.title} to the inputs, the modal loads, with blank fields, but which update with the saved value when I click inside the input field.当我尝试这个时,通过删除 defaultValue 并添加: value={issueGroup?.title} 到输入,模态加载,带有空白字段,但是当我在输入字段内单击时,它会更新为保存的值。

However, I can't update the value even if this UX issue were okay (it isn't).但是,即使这个 UX 问题没问题(但不是),我也无法更新该值。 The field becomes fixed and I can't type in it.该字段变得固定,我无法输入。

Third suggestion - being to define the defaultValues by using setValue.第三个建议- 使用 setValue 来定义默认值。 To implement this, I tried:为了实现这一点,我尝试了:

const { setValue } = useForm();
  
  
  const defaultValues = {
    title: setValue("title", selectedIssueGroup?.title),
    description: setValue("description", selectedIssueGroup?.description),
    // issues: "",
  }
  const form = useForm({ defaultValues, schema: IssueGroupSchema })

The modal loads (but with a blacked-out screen behind it, whereas other successful attempts just blur the background), but the saved values are not revealed in the input fields of the update form.模态加载(但后面有一个黑屏,而其他成功的尝试只是模糊背景),但保存的值不会显示在更新表单的输入字段中。

SANDBOX沙盒

I tried to make a sandbox for this form here .我试图在 这里为这个表格制作一个沙箱。 I don't know how to add the server connections to generate the types or mutations inside the code sandbox, so instead, I copied the prisma db extracts.我不知道如何添加服务器连接以在代码沙箱内生成类型或突变,所以我复制了 prisma db 提取物。

Try 1:尝试 1:

Possibly the state update completes after you open the modal because you wrote可能 state 更新在您打开模式后完成,因为您写了

setSelectedIssueGroup(issueGroup)
modalPropsUpdate.onOpen() // immediately opening modal

Here state update will take some time to set selectedIssueGroup .这里 state 更新需要一些时间来设置selectedIssueGroup before that modal gets opened and you have null as default state value so you might get the error.在打开该模式之前,您将null作为默认值 state,因此您可能会收到错误消息。

You need to open the modal once the state got set.设置 state 后,您需要打开模式。 You can achieve this using useEffect .您可以使用useEffect实现此目的。

useEffect(()=>{ 
  if(selectedIssueGroup) 
    modalPropsUpdate.onOpen() 
}, [selectedIssueGroup]);

Try 2:尝试 2:

if your modal gets mounted to DOM even before opening it, then your defaultValue will be set null. In that case you have to use value prop in <Input /> instead of defaultValue to set your values.如果您的模式甚至在打开它之前就已安装到 DOM,那么您的defaultValue将设置为 null。在这种情况下,您必须在<Input />中使用value prop 而不是defaultValue来设置您的值。

and handle null in input like并在输入中处理 null

<Input name="title" label="Title" defaultValue={selectedIssueGroup?.title} />

Try 3:尝试 3:

you can get the setValue from useForm() like this,您可以像这样从useForm()获取setValue

const { setValue } = useForm();

and use it to set any field value with name并使用它来设置名称的任何字段值

setValue("title", selectedIssueGroup.title);

To display the existing values in your form when it is in update mode, you can pass the existing values as the defaultValues to the useForm hook.要在更新模式下显示表单中的现有值,您可以将现有值作为默认值传递给 useForm 挂钩。 You can then retrieve these values using the form.values object within your form.然后,您可以在表单中使用 form.values object 检索这些值。

Here is an example of how you might modify your form to display the existing values:以下是您可以如何修改表单以显示现有值的示例:

const IssueGroupForm = ({ issueGroup }: { issueGroup: IssueGroupGQLType | null }) => {
  const form = useForm({
    defaultValues: issueGroup
      ? {
          title: issueGroup.title,
          description: issueGroup.description,
          // issues: issueGroup.issues,
        }
      : {
          title: '',
          description: '',
          // issues: '',
        },
    schema: IssueGroupSchema,
  });

  return (
    <Form onSubmit={form.handleSubmit}>
      <Input
        name="title"
        label="Title"
        placeholder="Enter the title"
        value={form.values.title}
        onChange={form.handleChange}
        error={form.errors.title}
      />
      <Textarea
        name="description"
        label="Description"
        placeholder="Enter the description"
        value={form.values.description}
        onChange={form.handleChange}
        error={form.errors.description}
      />
      {/* <Input
        name="issues"
        label="Issues"
        placeholder="Enter the issues"
        value={form.values.issues}
        onChange={form.handleChange}
        error={form.errors.issues}
      /> */}
      <ButtonGroup spacing={4}>
        <Button type="submit" variantColor="blue">
          {issueGroup ? 'Update' : 'Create'}
        </Button>
        {issueGroup && (
          <Button
            onClick={() => {
              setSelectedIssueGroup(null);
              onClose();
            }}
          >
            Cancel
          </Button>
        )}
      </ButtonGroup>
      {form.isSubmitting && <FormError error={form.error} />}
    </Form>
  );
};

In this example, the defaultValues are determined based on whether an issueGroup object is passed to the form.在此示例中,默认值是根据是否将问题组 object 传递给表单来确定的。 If an issueGroup is passed, the form will use the values from that object as the default values.如果传递了 issueGroup,表单将使用 object 中的值作为默认值。 If no issueGroup is passed, the form will use the default values specified in the defaultValues object.如果没有传递 issueGroup,表单将使用 defaultValues 中指定的默认值 object。

You can then pass the issueGroup object to the form when you want to display the existing values, like this:当您想要显示现有值时,您可以将 issueGroup object 传递给表单,如下所示:

<IssueGroupForm issueGroup={selectedIssueGroup} />

This will display the existing values in the form when it is in update mode, and display blank fields when it is in create mode.这将在更新模式下显示表单中的现有值,并在创建模式下显示空白字段。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM