Как реализовать react-sortable-ho c с Apollo graphql? - PullRequest
2 голосов
/ 06 мая 2020

Ссылка с образцом проблемы в формате gif здесь!

Здравствуйте, я создаю приложение, которое позволяет пользователям создавать списки (все данные хранятся на mongoDB и доступ через Apollo GraphQL); Каждый список имеет свойство listItems , массив, в котором хранятся все элементы в списке.

Когда пользователь переходит на страницу отдельных компонентов Ranklist. js, он загружает список со всеми элементами списка, доступ к которым осуществляется через запрос graphQL.

Отсюда приложение использует react-sortable-ho c, чтобы пользователи могли перемещать элементы списка. Хотя я могу перемещать listItems, как только я разрешаю go, страница сбрасывает порядок в соответствии с порядком массива, хранящегося на mongoDB.

Пожалуйста, сообщите мне, почему это case И как я могу правильно реализовать способ сохранения порядка списка с помощью response-sortable-ho c в сочетании с базой данных через Apollo graphQL.

import React, { useContext, 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,
    },
  });


  const [submitListItem] = useMutation(SUBMIT_LIST_ITEM_MUTATION, {
    update() {
      setListItem("");
      listItemInputRef.current.blur();
    },
    variables: {
      listId,
      body: listItem,
    },
  });

  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),
    }));
  }

  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={listItems}
            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 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 Ответ

2 голосов
/ 06 мая 2020

Ваша onSortEnd функция вызывает setState и обновляет state.items соответственно, но тогда вы никогда не используете state.items в своем компоненте - вместо этого вы используете data.getList.listItems напрямую. Поскольку это значение не меняется после первоначальной выборки, ваш список останется прежним.

Вместо этого вы должны использовать state.items. Затем вы можете использовать useEffect для обновления state.items всякий раз, когда значение listItems изменяется - например, первоначально, когда возвращается ответ от сервера или в ответ на мутацию. Что-то вроде:

onEffect(() => {
  if (data && data.getList && data.getList.listItems) {
    setState(() => ({ items: data.getList.listItems }))
  }
}, [data])
...