【问题标题】:How to implement react-sortable-hoc with Apollo graphql?如何使用 Apollo graphql 实现 react-sortable-hoc?
【发布时间】:2020-08-20 23:01:19
【问题描述】:

Link with sample gif of issue here!

您好,我正在创建一个允许用户创建列表的应用程序(所有数据都存储在 mongoDB 上并通过 Apollo GraphQL 访问);每个列表都有一个属性 listItems,这是一个存储列表中所有项目的数组。

当用户进入个人 Ranklist.js 组件页面时,它会加载列表,其中包含通过 graphQL 查询访问的所有列表项。

从这里开始,应用程序利用react-sortable-hoc 允许用户移动列表项。虽然我可以移动 listItems,但只要我放手,页面就会重置顺序以匹配存储在 mongoDB 上的数组的顺序。

请告诉我为什么会出现这种情况,以及我如何正确实施一种方法,通过 Apollo graphQL 与数据库一起使用 react-sortable-hoc 保存列表的顺序。

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;

【问题讨论】:

    标签: reactjs graphql apollo-server react-sortable-hoc


    【解决方案1】:

    您的onSortEnd 函数调用setState 并相应地更新state.items,但是您永远不会在组件中使用state.items——而是直接使用data.getList.listItems。由于该值在最初获取后不会更改,因此您的列表保持不变。

    您应该改用state.items。然后,您可以在listItems 的值发生变化时使用useEffect 更新state.items——例如,最初是在返回来自服务器的响应或响应突变时。比如:

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

    【讨论】:

    • 非常感谢您的回复。我对此很陌生,你会建议我把onEffect放在哪里?另外,当我最初加载这个组件时,我将如何给出 state.items 的值,我的 mongoDB 中的所有 listItems (有没有办法插入它在这种情况下最初是通过 setState 吗?)由于某种原因,我在尝试以下操作时收到“数据未定义”: const { loading, error, data } = useQuery(FETCH_LIST_QUERY, { variables: { listId, }, }); const [state, setState] = useState({ items: data.getList.listItems });
    • data 最初是未定义的,因为获取查询是异步的。像您目前正在做的那样,将 items 初始化为一个空数组。在声明其他钩子后放置useEffect。因为我们将[data] 作为第二个参数传递给useEffect,所以我们作为第一个参数传入的函数将在每次数据变化时被调用。见here
    猜你喜欢
    • 2019-01-18
    • 2018-04-01
    • 2020-03-21
    • 2020-04-30
    • 1970-01-01
    • 2018-05-02
    • 2019-07-24
    • 1970-01-01
    • 2020-02-05
    相关资源
    最近更新 更多