如何使用 Apollo graphql 实现 react-sortable-hoc?

Posted

技术标签:

【中文标题】如何使用 Apollo graphql 实现 react-sortable-hoc?【英文标题】:How to implement react-sortable-hoc with Apollo graphql? 【发布时间】: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;

【问题讨论】:

【参考方案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。

以上是关于如何使用 Apollo graphql 实现 react-sortable-hoc?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 NestJS GraphQL 实现 NuxtJS Apollo

如何使用 apollo ios 客户端实现 graphql 订阅

如何使“apollo 服务器”与 graphql schemaObject 一起使用?

如何使用 Apollo graphql 实现 react-sortable-hoc?

如何使用 apollo 服务器从变量运行 graphql 查询

如何使用 apollo 客户端在 graphql 的角度处理错误?