Я создаю приложение, которое позволяет пользователям создавать списки (все данные хранятся на mongoDB и доступны через Apollo GraphQL); Каждый список имеет свойство listItems - массив, в котором хранятся все элементы в списке.
В настоящее время я могу перемещать listItems и иметь их setState в массиве, доступном через state.items, как показано в этом видео . Перемещение списка контролируется компонентами, находящимися в react-sortable-ho c library
Проблема, с которой я сталкиваюсь, заключается в том, чтобы взять этот массив state.items из listItems и использовать мутацию, чтобы иметь массив state.items заменяет соответствующий бэкэнд массив list.listItems в базе данных.
Я считаю, что, возможно, я неправильно настроил typeDefs для входных ListItems (я получал Error: Тип Mutation.editListItems (listItems :) должен быть типом ввода, но получил: [ListItem] !. поэтому я изменил его на input вместо типа, который может быть проблемой?)
Наконец, я пытаюсь вызвать мутацию editListItems в функциональном компоненте RankList ниже (содержится в RankList. js) 'onChange '. Я не уверен, в чем проблема и при перемещении предметов, похоже, мутация не вызывается. Пожалуйста, посоветуйте (дайте мне знать, если мне нужно добавить больше информации)!
В целом, мне интересно, может ли следующее быть проблемой:
- Правильно ли реализован typeDef для editListItems?
- Правильно ли здесь использовать onChange?
- Правильно ли я вызываю мутацию (в компоненте RankList)?
- Правильно ли я передаю состояние .items в мутации editListItems (в компоненте 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;