设计表单构建器的 db 和 state 变化和请求,以与 graphQL、faunaDB、nextJS 和阿波罗 [英] Designing the db and state mutations and requests of a form builder in react with graphQL, faunaDB, nextJS & Apollo

查看:23
本文介绍了设计表单构建器的 db 和 state 变化和请求,以与 graphQL、faunaDB、nextJS 和阿波罗的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发下一个 JS、React、Apollo、graphQL、faunaDB 应用程序.我正在尝试构建表单构建器如何通过 graphQL 处理它对动物群的突变.我能够从操场运行突变,并且可以从前端查询并构建我的表单.此处看到的交互https://www.loom.com/share/7f7d1e1231d445f2be6b239b6c81

现在我很确定我可以弄清楚如何在前端运行一个突变,我关心的是什么时候运行它?请参阅我有以下代码.查询 faunaDB,并从状态(由查询填充)输出表单输入元素(当前只是文本类型的元素),并允许您向状态添加新的表单输入类型,从而导致重新渲染并显示新的表单输入元素.这一切都很好.

import { useQuery, gql } from "@apollo/client";import { useEffect, useState } from react";从uuid"导入 { v4 as uuidv4 };const INPUT_VALUES = gql`查询 GetInputValues {allFormInputVals {数据 {姓名_ID类型}}}`;const Home = () =>{const { 加载,错误,数据 } = useQuery(INPUT_VALUES);const [formState, setFormState] = useState(undefined);useEffect(() => {setFormState(data?.allFormInputVals?.data);}, [数据]);const addInput = () =>{const 空白文本 = {__typename: "FormInputType",名称:产品图片",_id: uuidv4(),类型:文本",};控制台日志(formState);setFormState([...formState, { ...blanktext }]);};if (loading) return <p>Loading...</p>;if (error) return <p>Error: {error.message}</p>;返回 (<><表格><输入类型=按钮"值=添加表单输入"onClick={addInput}/>{formState?.map((val, idx) => {const nameId = `name-${idx}`;const typeId = `type-${idx}`;返回 (<div key={val._id}>{val.type === 文本"&&(<><label htmlFor={nameId}>{`Name #${idx + 1}`}</label><输入类型=文本"名称={nameId}id={nameId}类名={val.type}/><label htmlFor={typeId}>{`Type #${idx + 1}`}</label><选择名称={typeId} id={typeId} className={val.type}>{data.allFormInputVals.data.map((item) => {返回 (<option key={item._id} value={item.type}>{物品种类}</选项>);})}</选择></>)}

);})}</表单></>);};导出默认首页;

然而,我在操场上的变异选项似乎仅限于一次只添加一个所谓的文档,即一个对象.如果我控制台记录我想添加到数据库的状态,它看起来如下.现在我可以在操场上做一个突变,在那里我可以添加这些对象之一.但我希望能够一次添加所有这些.在保存操作上.我想这样做是因为我不想运行对表单的每个添加的请求,我想使用反应状态来处理表单直到最后,然后执行我的数据库请求.

<预><代码>[{__typename":FormInputVal",名称":名称",_id":291541872966369805",类型":文本"},{__typename":FormInputVal",名称":名称",_id":291541888089981453",类型":文本"},{__typename":FormInputVal",名称":产品图片",_id":255f95e0-bff1-4e75-81fc-d6f3f9a72446",类型":文本"}]

现在我已经创建了一个具有用户的 graphQL 模式,它可以有很多表单,表单可以有很多输入.看起来是这样.@relation 指令专用于 faunaDB.除了我一直提到的这个突变问题之外,它的工作方式符合我的预期.

type 表单 {名称:字符串!指数:整!用户:用户formInputVals: [FormInputVal!] @relation}输入 FormInputVal {名称:字符串!指数:整!类型:字符串!formRoot:表格!}输入用户{名称:字符串!电子邮件:字符串!密码:字符串!表格:[表格] @relation}类型查询{allForms:[表格!]所有用户:[用户!]allFormInputVals: [FormInputVal!]}

看到我可以用以下方法改变数据库.我选择一个特定的表单并添加一个输入,从而导致前端的重新渲染并显示表单输入.这一切都很好.这是该类型的一个示例变异.

变异{createFormInputVal(数据:{formRoot:{connect:"291541554941657608"},name:"name",type:"text",index:0}){姓名类型指数表单根{姓名}}}

但这就是问题的根源所在.

我想获取由 react 创建的状态,并将其添加到名为 formInputVal 的 faunaDB 集合中,graphql 模式映射到 db 集合.

我与 Fauna 支持人员进行了交谈,他们提到了一个 @resolver 指令,我可以在其中运行 DB 函数并一次添加多个文档(对象),到目前为止,faunaDB 的 lambda 函数语法超出了我的理解.他们提到了这篇文章的功能 https://docs.fauna.com/fauna/current/tutorials/ecommerce#function 和这个解析器 https://forums.fauna.com/t/placing-an-index-on-a-field-in-an-embedded-type/778/4

让我们澄清一下,

我这样做对吗?我愿意更改架构.如果您想使用替代方法或相同的方法解决此问题,但缺少我不明白的部分,您会怎么做.

为什么我不能只将一组对象传递给正确的 formID 的突变,并且它会在一个查询中将那么多文档添加到集合中.是否有任何类型的通用实践来创建这样的生成形式.

好的,提前感谢您的帮助.

更新:

我尝试了以下突变,但它不起作用.它出现以下错误 Unknown argument 'formInputVal' on field 'createFormInputVal' of type 'Mutation'

const ADD_INPUT_VALUES = gql`突变 AddInputValues($formInputVal: FormInputValInput!) {createFormInputVal(formInputVal: $formInputVal) {姓名}}`;const [createFormInputVal, { data: createInputData }] = useMutation(ADD_INPUT_VALUES);...<表格onSubmit={async (e) =>{e.preventDefault();const res = await createFormInputVal({变量:formState,}).catch(console.error);控制台日志(res);}}>...

解决方案

如果这里问题的根源是改变或同时创建多个文档,我认为它在这里重复了:

https://stackoverflow.com/a/68930202/534056

需要注意的几点:

  • 为您的自定义输入选择一个不等于 [type name] + Input 的名称.这就是 Fauna 用于自动生成的 CRUD 操作的内容,如果您自己定义一个,它将覆盖生成的一个.例如,给定类型 Form,Fauna 将生成一个名为 FormInput 的输入类型,因此不要将其用于自定义类型.

  • 如果要将数据数组作为输入传递,请将参数指定为 List 类型(置于方括号中).

  • 如果您要更新某些文档,则需要传入 ID.GraphQL API 隐藏了一些关于 Refs 的细节,因此在您的 UDF 中,您需要根据 ID 重建 ref.

I'm working on a next JS, React, Apollo, graphQL, faunaDB App. I am trying to architect how a form builder would work with it's mutations to fauna via graphQL. I am able to run mutations from the playground, and can query from the front end and build my form. Interaction seen here https://www.loom.com/share/7f7d1e1231d445f2be6b5db2c81239b6

Now I am pretty sure I can figure out how to run a mutation on the front end, my concern is when to run it? See I have the following code. Which queries faunaDB, and outputs form input elements from the state (which is populated by the query) (currently just ones of the type text) and allows you to add new form input types to the state causing a rerender and displaying a new form input element. This is all well and good.

import { useQuery, gql } from "@apollo/client";
import { useEffect, useState } from "react";
import { v4 as uuidv4 } from "uuid";

const INPUT_VALUES = gql`
  query GetInputValues {
    allFormInputVals {
      data {
        name
        _id
        type
      }
    }
  }
`;

const Home = () => {
  const { loading, error, data } = useQuery(INPUT_VALUES);

  const [formState, setFormState] = useState(undefined);

  useEffect(() => {
    setFormState(data?.allFormInputVals?.data);
  }, [data]);

  const addInput = () => {
    const blanktext = {
      __typename: "FormInputType",
      name: "Product Image",
      _id: uuidv4(),
      type: "text",
    };
    console.log(formState);
    setFormState([...formState, { ...blanktext }]);
  };

  if (loading) return <p>Loading...</p>;

  if (error) return <p>Error: {error.message}</p>;

  return (
    <>
      <form>
        <input type="button" value="Add Form Input" onClick={addInput} />
        {formState?.map((val, idx) => {
          const nameId = `name-${idx}`;
          const typeId = `type-${idx}`;
          return (
            <div key={val._id}>
              {val.type === "text" && (
                <>
                  <label htmlFor={nameId}>{`Name #${idx + 1}`}</label>

                  <input
                    type="text"
                    name={nameId}
                    id={nameId}
                    className={val.type}
                  />
                  <label htmlFor={typeId}>{`Type #${idx + 1}`}</label>

                  <select name={typeId} id={typeId} className={val.type}>
                    {data.allFormInputVals.data.map((item) => {
                      return (
                        <option key={item._id} value={item.type}>
                          {item.type}
                        </option>
                      );
                    })}
                  </select>
                </>
              )}
            </div>
          );
        })}
      </form>
    </>
  );
};

export default Home;

However my mutation options in the playground seem to be limited to only adding one what is called a document at a time, which is one object. If I console log my state that I want to add to the db it looks as follows. Now I can do a mutation in the playground where I can add one of these objects. But I want to be able to add all of them at once. On a save operation. I want to do this because I dont want to run a request of every addition to the form, I want to use react state to handle the form until the very end and then do my db request.

[
   {
      "__typename":"FormInputVal",
      "name":"name",
      "_id":"291541872966369805",
      "type":"text"
   },
   {
      "__typename":"FormInputVal",
      "name":"name",
      "_id":"291541888089981453",
      "type":"text"
   },
   {
      "__typename":"FormInputVal",
      "name":"Product Image",
      "_id":"255f95e0-bff1-4e75-81fc-d6f3f9a72446",
      "type":"text"
   }
]

Now I have created a graphQL schema the has Users, which can have many Forms, and Forms which can have many inputs. It looks like so. The @relation directive is specifice to faunaDB. It works how I expect it to, beyond this mutation issue I have been mentioning.

type Form {
  name: String!
  index: Int!
  user: User
  formInputVals: [FormInputVal!] @relation
}

type FormInputVal {
  name: String!
  index: Int!
  type: String!
  formRoot: Form!
}

type User {
  name: String!
  email: String!
  password: String!
  forms: [Form] @relation
}

type Query {
  allForms: [Form!]
  allUsers: [User!]
  allFormInputVals: [FormInputVal!]
}

See I can mutate the DB with the following. Where I select a specific form and add a input, thus causing a rerender of the frontend and the form input shows up. This is all well and great. This is an example muation of that type.

mutation{
  createFormInputVal(data:
    {formRoot:{connect:"291541554941657608"},name:"name",type:"text",index:0}){
    name
    type
    index
    formRoot{
      name
    }
  }
}

But here is where the root of the problem lies.

I want to take that state created by react and add it to a faunaDB collection which is called formInputVal the graphql schema is mapped to the db collections.

I talked to Fauna support and they mentioned a @resolver directive where I can run a DB function and add multiple documents(objects) at once so far the lambda function syntax for faunaDB is above my understanding. They mentioned this article for the function https://docs.fauna.com/fauna/current/tutorials/ecommerce#function and this one for the resolver https://forums.fauna.com/t/placing-an-index-on-a-field-in-an-embedded-type/778/4

Let's clarify,

Am I approaching this right? I am open to changing the schema. What would you do if you wanted to solve this problem with alternative approches or the same approach, but with a missing piece I don't understand.

Why can't I just pass an array of objects to the mutation for the correct formID, and it add's that many document's to the collection in one query. Is there any sort of general pratice for creating a generative form like this.

Ok thanks any help ahead of time.

UPDATE:

I have tried the following mutation but it does not work. It haults with the following error Unknown argument 'formInputVal' on field 'createFormInputVal' of type 'Mutation'

const ADD_INPUT_VALUES = gql`
  mutation AddInputValues($formInputVal: FormInputValInput!) {
    createFormInputVal(formInputVal: $formInputVal) {
      name
    }
  }
`;

const [createFormInputVal, { data: createInputData }] = useMutation(
    ADD_INPUT_VALUES
  );

...
<form
        onSubmit={async (e) => {
          e.preventDefault();
          const res = await createFormInputVal({
            variables: formState,
          }).catch(console.error);
          console.log(res);
        }}
      >
...

解决方案

If the root of the issue here is mutating or creating multiple documents at once, I think it got duplicated here:

https://stackoverflow.com/a/68930202/534056

A couple of things to watch out for:

  • Choose a name for your custom inputs that does not equal [type name] + Input. This is what Fauna uses for the auto-generated CRUD operations, and if you define one yourself the it will override the generated one. For example, given the type Form, Fauna will generate an input type called FormInput, so don't use that for a custom one.

  • If you want to pass an array of data as input, specify the argument as a List type (placed in square brackets).

  • If you are updating some documents, you will need to pass in the ID. The GraphQL API hides some details around Refs, so in your UDF, you will need to reconstruct the ref from an ID.

这篇关于设计表单构建器的 db 和 state 变化和请求,以与 graphQL、faunaDB、nextJS 和阿波罗的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆