如何将具有多个对象的状态数组作为参数传递给graphql突变?
Posted
技术标签:
【中文标题】如何将具有多个对象的状态数组作为参数传递给graphql突变?【英文标题】:How to pass state array with multiple objects as a parameter to graphql mutation? 【发布时间】:2020-08-23 15:13:16 【问题描述】:我正在创建一个允许用户创建列表的应用程序(所有数据都存储在 mongoDB 上并通过 Apollo GraphQL 访问);每个列表都有一个属性 listItems,它是一个存储列表中所有项目的数组。
目前,我现在能够移动 listItems 并将它们 setState 在通过 state.items 访问的数组中,如 in this video 所示。列表移动由react-sortable-hoc library中的组件控制
我遇到的问题是使用 listItems 的 state.items 数组并使用突变让 state.items 数组替换数据库中后端相应的 list.listItems 数组。
我相信我可能没有正确设置输入 ListItems 的 typeDefs(我得到 错误:Mutation.editListItems(listItems:) 的类型必须是输入类型但得到:[ListItem]!。 所以我把它改成了 input 而不是 type 这可能是个问题?)
最后,我尝试在下面的 RankList 功能组件中调用 editListItems 突变(包含在 RankList.js 中)“onChange”。我不确定问题出在哪里以及在移动项目时,似乎没有调用突变。请告知(让我知道我是否应该包含更多信息)!
总的来说,我想知道以下是否可能是一个问题:
editListItems 的 typeDef 是否正确实现? 在这里使用 onChange 合适吗? 我是否正确调用了突变(在 RankList 组件中)? 我是否在 editListItems 突变中正确传递 state.items(在 RankList 组件中)?typeDefs.js
const gql = require("apollo-server");
//schema
module.exports = gql`
type List
id: ID!
title: String!
createdAt: String!
username: String!
listItems: [ListItem]!
comments: [Comment]!
likes: [Like]!
likeCount: Int!
commentCount: Int!
type ListItem
id: ID!
createdAt: String!
username: String!
body: String!
input ListItems
id: ID!
createdAt: String!
username: String!
body: String!
type Comment
id: ID!
createdAt: String!
username: String!
body: String!
type Like
id: ID!
createdAt: String!
username: String!
type User
id: ID!
email: String!
token: String!
username: String!
createdAt: String!
input RegisterInput
username: String!
password: String!
confirmPassword: String!
email: String!
type Query
getLists: [List]
getList(listId: ID!): List
type Mutation
register(registerInput: RegisterInput): User!
login(username: String!, password: String!): User!
createList(title: String!): List!
editListItems(listId: ID!, listItems: ListItems!): List!
deleteList(listId: ID!): String!
createListItem(listId: ID!, body: String!): List!
deleteListItem(listId: ID!, listItemId: ID!): List!
createComment(listId: ID!, body: String!): List!
deleteComment(listId: ID!, commentId: ID!): List!
likeList(listId: ID!): List!
,
type Subscription
newList: List!
`;
list.js(解析器)
async editListItems(_, listId, listItems , context)
console.log("editListItems Mutation activated!");
const user = checkAuth(context);
const list = await List.findById(listId);
if (list)
if (user.username === list.username)
list.listItems = listItems;
await list.save();
return list;
else
throw new AuthenticationError("Action not allowed");
else
throw new UserInputError("List not found");
,
RankList.js(上面视频中显示的列表组件)
import React, useContext, useEffect, useRef, useState from "react";
import gql from "graphql-tag";
import useQuery, useMutation from "@apollo/react-hooks";
import Form from "semantic-ui-react";
import moment from "moment";
import AuthContext from "../context/auth";
import SortableContainer, SortableElement from "react-sortable-hoc";
import arrayMove from "array-move";
import "../RankList.css";
import CSSTransitionGroup from "react-transition-group";
const SortableItem = SortableElement(( value ) => (
<li className="listLI">value</li>
));
const SortableList = SortableContainer(( items ) =>
return (
<ol className="theList">
<CSSTransitionGroup
transitionName="ranklist"
transitionEnterTimeout=500
transitionLeaveTimeout=300
>
items.map((item, index) => (
<SortableItem
key=`item-$item.id`
index=index
value=item.body
/>
))
</CSSTransitionGroup>
</ol>
);
);
function RankList(props)
const listId = props.match.params.listId;
const user = useContext(AuthContext);
const listItemInputRef = useRef(null);
const [state, setState] = useState( items: [] );
const [listItem, setListItem] = useState("");
const loading, error, data = useQuery(FETCH_LIST_QUERY,
variables:
listId,
,
onError(err)
console.log(err.graphQLErrors[0].extensions.exception.errors);
// setErrors(err.graphQLErrors[0].extensions.exception.errors);
);
useEffect(() =>
if (data && data.getList && data.getList.listItems)
setState(() => ( items: data.getList.listItems ));
, [data]);
// const [state, setState] = useState( items: data.getList.listItems );
const [submitListItem] = useMutation(SUBMIT_LIST_ITEM_MUTATION,
update()
setListItem("");
listItemInputRef.current.blur();
,
variables:
listId,
body: listItem,
,
);
const [editListItems] = useMutation(EDIT_LIST_ITEMS_MUTATION,
variables:
listId,
listItems: state.items,
,
);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error..</p>;
function deleteListCallback()
props.history.push("/");
function onSortEnd( oldIndex, newIndex )
setState(( items ) => (
items: arrayMove(items, oldIndex, newIndex),
));
//THIS MAY BE WHERE THE ISSUE LIES
editListItems();
let listMarkup;
if (!data.getList)
listMarkup = <p>Loading list...</p>;
else
const
id,
title,
createdAt,
username,
listItems,
comments,
likes,
likeCount,
commentCount,
= data.getList;
listMarkup = user ? (
<div className="todoListMain">
<div className="rankListMain">
<div className="rankItemInput">
<h3>title</h3>
<Form>
<div className="ui action input fluid">
<input
type="text"
placeholder="Choose rank item.."
name="listItem"
value=listItem
onChange=(event) => setListItem(event.target.value)
ref=listItemInputRef
/>
<button
type="submit"
className="ui button teal"
disabled=listItem.trim() === ""
onClick=submitListItem
>
Submit
</button>
</div>
</Form>
</div>
<SortableList
items=state.items
onSortEnd=onSortEnd
helperClass="helperLI"
/>
</div>
</div>
) : (
<div className="todoListMain">
<div className="rankListMain">
<div className="rankItemInput">
<h3>props.title</h3>
</div>
<SortableList
items=listItems
onSortEnd=onSortEnd
helperClass="helperLI"
/>
</div>
</div>
);
return listMarkup;
const EDIT_LIST_ITEMS_MUTATION = gql`
mutation($listId: ID!, $listItems: ListItems!)
editListItems(listId: $listId, listItems: $listItems)
id
listItems
id
body
createdAt
username
`;
const SUBMIT_LIST_ITEM_MUTATION = gql`
mutation($listId: ID!, $body: String!)
createListItem(listId: $listId, body: $body)
id
listItems
id
body
createdAt
username
comments
id
body
createdAt
username
commentCount
`;
const FETCH_LIST_QUERY = gql`
query($listId: ID!)
getList(listId: $listId)
id
title
createdAt
username
listItems
id
createdAt
username
body
likeCount
likes
username
commentCount
comments
id
username
createdAt
body
`;
export default RankList;
【问题讨论】:
【参考方案1】:太多的问题......简单地说:
const [state, setState] = useState( items: [] );
不要在钩子中使用setState
...这是误导,setState
用于类组件。
使用一些有意义的名称,例如 f.e. itemList
:
const [itemList, setItemList] = useState( [] ); // just array
调整查询结果保存:
useEffect(() =>
if (data && data.getList && data.getList.listItems)
// setState(() => ( items: data.getList.listItems ));
setItemList( data.getList.listItems );
, [data]);
调整可排序的更新处理程序:
const onSortEnd = (oldIndex, newIndex) =>
// this.setState((items) => (
// items: arrayMove(items, oldIndex, newIndex),
// ));
const newListOrder = arrayMove( itemList, oldIndex, newIndex);
// console.log("array sorted", newListOrder );
// update order in local state
// kind of optimistic update
setItemList( newListOrder );
// update remote data
editListItems(
variables:
listId,
listItems: newListOrder,
);
将onSortEnd
处理程序传递给可排序的:
<SortableList items=itemList onSortEnd=onSortEnd />
Mutation 应该有 update
处理程序(writeQuery
- 阅读文档)来强制查询更新/重新渲染。 在这种情况下并不完全需要(数组已经排序为可排序的,我们正在更新本地数组,它将重新渲染)但它应该在这里(突变失败/错误 - 处理来自 useMutation
的 error
)。
类型很好...但是 api 检测到错误的类型 ... 数组中的每个项目都包含 __typename
属性 ...迭代 newListOrder
元素( fe 使用.map()
) 并删除__typename
。它应该在console.log(newListOrder)
中可见。
在 Playground (/graphiql
) 中尝试后端/API ...在编码前端之前尝试使用变量突变。比较网络请求/响应详细信息。
【讨论】:
非常感谢 xadm 的回复。当我尝试创建您所显示的类型时 input ListItemsInput: [ListItem]! 我收到服务器错误。该输入是否应该以不同的方式书写? (我还有很多问题要问你,请耐心等待!) 查看更新,检查传递的变量,网络请求,在 Playground 中尝试突变。 嘿@xadm 看看我最近的问题。我收到未处理的拒绝(错误):网络错误:响应不成功:收到状态代码 400。我还尝试在操场上使用突变,但它不会让我按下播放!有问题。 ***.com/questions/61692553/… 如回答中所述删除__typename
playground 问题 - 在查询下定义变量(输入内容)对象?以上是关于如何将具有多个对象的状态数组作为参数传递给graphql突变?的主要内容,如果未能解决你的问题,请参考以下文章
我可以将数组作为参数传递给 Java 中具有可变参数的方法吗?