无法识别对象 - 在 graphql apollo 客户端中出现错误

Could not identify object - getting error in graphql apollo client

我是 graphql 的新手,我正在构建实时聊天应用程序。目前我先让它离线。 使用 React 作为前端。

我目前正在使用 apollo3-cache-persist 在 localStorage 上缓存数据。但是我如何查询缓存数据而不是服务器(当我离线时)我还想在我离线时将消息添加到 localStorage。

设备在线时显示乐观响应 我想将待处理的数据发送到服务器。

我的 ApolloProvider.js 客户端文件夹中的文件

import React from "react";
import {
  ApolloClient,
  InMemoryCache,
  ApolloProvider as Provider,
  createHttpLink,
  ApolloLink,
  split,
} from "@apollo/client";
import { setContext } from "@apollo/client/link/context";
import { RetryLink } from "@apollo/client/link/retry";
import { persistCache, LocalStorageWrapper } from "apollo3-cache-persist";
import { WebSocketLink } from "@apollo/client/link/ws";
import { getMainDefinition } from "@apollo/client/utilities";
import QueueLink from "apollo-link-queue";

let httpLink = createHttpLink({
  uri: "http://localhost:4000/",
});

const authLink = setContext((_, { headers }) => {
  const token = localStorage.getItem("token");
  // return the headers to the context so httpLink can read them
  return {
    headers: {
      ...headers,
      authorization: token ? `Bearer ${token}` : "",
    },
  };
});

httpLink = authLink.concat(httpLink);

const wsLink = new WebSocketLink({
  uri: `ws://localhost:4000/`,
  options: {
    reconnect: true,
    connectionParams: {
      Authorization: `Bearer ${localStorage.getItem("token")}`,
    },
  },
});
const link = new RetryLink();
const queueLink = new QueueLink();
const splitLink = split(
  ({ query }) => {
    const definition = getMainDefinition(query);
    return (
      definition.kind === "OperationDefinition" &&
      definition.operation === "subscription"
    );
  },
  wsLink,
  httpLink
);

const cache = new InMemoryCache();

const fun = async () =>
  await persistCache({
    cache,
    storage: new LocalStorageWrapper(window.localStorage),
  });
fun();

const client = new ApolloClient({
  // link: splitLink,
  link: ApolloLink.from([splitLink, queueLink, link]),
  cache,
  name: "chat-app",
  version: "1.0.0",
  queryDeduplication: false,
  defaultOptions: {
    watchQuery: {
      fetchPolicy: "cache-and-network",
    },
  },
});

export default function ApolloProvider(props) {
  return <Provider client={client} {...props} />;
}

我的messages.js文件

import React, { Fragment, useEffect, useState } from "react";
import { gql, useLazyQuery, useMutation, InMemoryCache } from "@apollo/client";
import { Col, Form } from "react-bootstrap";
import { useMessageDispatch, useMessageState } from "../../context/message";
import uuid from "react-uuid";
import Message from "./Message";

const SEND_MESSAGE = gql`
  mutation sendMessage($uuid: String, $to: String!, $content: String!) {
    sendMessage(uuid: $uuid, to: $to, content: $content) {
      uuid
      from
      to
      content
      createdAt
      hasSeen
      hasSent
    }
  }
`;

const GET_MESSAGES = gql`
  query getMessages($from: String!) {
    getMessages(from: $from) {
      uuid
      from
      to
      content
      createdAt
      hasSeen
    }
  }
`;

export default function Messages() {
  const { users } = useMessageState();
  const dispatch = useMessageDispatch();
  const [content, setContent] = useState("");

  const selectedUser = users?.find((u) => u.selected === true);
  const messages = selectedUser?.messages;

  const [getMessages, { loading: messagesLoading, data: messagesData }] =
    useLazyQuery(GET_MESSAGES, {
      update(cache) {
        cache.readFragment({});
        console.log("reading");
      },
    });

  const [sendMessage] = useMutation(SEND_MESSAGE, {
    update(cache, { data: { sendMessage } }) {
      cache.modify({
        fields: {
          getMessages(existingMsg) {
            console.log(existingMsg);
            const newMsgRef = cache.writeFragment({
              data: sendMessage,
              fragment: gql`
                fragment sendNewMessage on Mutation {
                  uuid
                  to
                  from
                  content
                  hasSeen
                  hasSent
                }
              `,
            });
            return existingMsg.push(newMsgRef);
          },
        },
      });
    },
    onError: (err) => console.log(err),
  });

  useEffect(() => {
    if (selectedUser && !selectedUser.messages) {
      getMessages({ variables: { from: selectedUser.username } });
    }
  }, [selectedUser]);

  useEffect(() => {
    if (messagesData) {
      dispatch({
        type: "SET_USER_MESSAGES",
        payload: {
          username: selectedUser.username,
          messages: messagesData.getMessages,
        },
      });
    }
  }, [messagesData]);

  const submitMessage = (e) => {
    e.preventDefault();
    if (content.trim() === "" || !selectedUser) return;
    let id = uuid();

    sendMessage({
      variables: { uuid: id, to: selectedUser.username, content },
      optimisticResponse: {
        sendMessage: {
          __typename: "Mutation",
          uuid: id,
          from: "User",
          to: selectedUser.username,
          content,
          hasSent: false,
          hasSeen: false,
          createdAt: Date.now(),
        },
      },
    });

    setContent("");
  };

  // Displaying helper text and styling
  let selectedChatMarkup;
  if (!messages && !messagesLoading) {
    selectedChatMarkup = <p className="info-text"> Select a friend</p>;
  } else if (messagesLoading) {
    selectedChatMarkup = <p className="info-text"> Loading..</p>;
  } else if (messages.length > 0) {
    selectedChatMarkup = messages.map((message, index) => (
      <Fragment key={message.uuid}>
        <Message message={message} />
        {index === messages.length - 1 && (
          <div className="invisible">
            <hr className="m-0" />
          </div>
        )}
      </Fragment>
    ));
  } else if (messages.length === 0) {
    selectedChatMarkup = (
      <p className="info-text">
        You are now connected! send your first message!
      </p>
    );
  }

  return (
    <Col xs={10} md={8}>
      <div className="messages-box d-flex flex-column-reverse">
        {selectedChatMarkup}
      </div>
      <div>
        <Form onSubmit={submitMessage}>
          <Form.Group className="d-flex align-items-center">
            <Form.Control
              type="text"
              className="message-input rounded-pill p-4 bg-secondary border-0"
              placeholder="Type a message.."
              value={content}
              onChange={(e) => setContent(e.target.value)}
            />
            <i
              className="fas fa-regular fa-paper-plane fa-2x text-primary ml-2"
              onClick={submitMessage}
              role="button"
            ></i>
          </Form.Group>
        </Form>
      </div>
    </Col>
  );
}

但我目前在尝试发送消息时遇到此错误

react_devtools_backend.js:3973 Invariant Violation: Could not identify object {"uuid":"4855ffc-6b7b-d7c8-a68-2ae0162f80a","from":"User","to":"Fire","content":"example text","createdAt":1648881891383,"hasSeen":false,"hasSent":false,"__typename":"Mutation"}

也从突变错误日志中得到错误

Error: Could not identify object {"__typename":"Message","uuid":"4855ffc-6b7b-d7c8-a68-2ae0162f80a","from":"Alan","to":"Fire","content":"example text","createdAt":"2022-04-02T06:44:51.807Z","hasSeen":false,"hasSent":false}

Apollo 使用 default __typenameid 字段来标准化缓存。因此,在您的情况下,Apollo 不会将您的 uuid 属性 识别为缓存标识符。您可以更改 uuid uuid 属性,或将 keyFields 添加到您的 InMemoryCache 配置。