我在创建新评论时使用更新后更新来更新商店 . 我也订阅了此页面上的评论 .
这些方法中的任何一种都可以按预期方式工作 . 但是当我同时拥有它们时,创建注释的用户将在页面上看到两次注释并从React获取此错误:
Warning: Encountered two children with the same key,
我认为原因是突变更新和订阅都返回一个新节点,创建一个重复的条目 . 有没有推荐的解决方案?我在Apollo文档中看不到任何内容,但它对我来说似乎不是一个边缘用例 .
这是我订阅的组件:
import React from 'react';
import { graphql, compose } from 'react-apollo';
import gql from 'graphql-tag';
import Comments from './Comments';
import NewComment from './NewComment';
import _cloneDeep from 'lodash/cloneDeep';
import Loading from '../Loading/Loading';
class CommentsEventContainer extends React.Component {
_subscribeToNewComments = () => {
this.props.COMMENTS.subscribeToMore({
variables: {
eventId: this.props.eventId,
},
document: gql`
subscription newPosts($eventId: ID!) {
Post(
filter: {
mutation_in: [CREATED]
node: { event: { id: $eventId } }
}
) {
node {
id
body
createdAt
event {
id
}
author {
id
}
}
}
}
`,
updateQuery: (previous, { subscriptionData }) => {
// Make vars from the new subscription data
const {
author,
body,
id,
__typename,
createdAt,
event,
} = subscriptionData.data.Post.node;
// Clone store
let newPosts = _cloneDeep(previous);
// Add sub data to cloned store
newPosts.allPosts.unshift({
author,
body,
id,
__typename,
createdAt,
event,
});
// Return new store obj
return newPosts;
},
});
};
_subscribeToNewReplies = () => {
this.props.COMMENT_REPLIES.subscribeToMore({
variables: {
eventId: this.props.eventId,
},
document: gql`
subscription newPostReplys($eventId: ID!) {
PostReply(
filter: {
mutation_in: [CREATED]
node: { replyTo: { event: { id: $eventId } } }
}
) {
node {
id
replyTo {
id
}
body
createdAt
author {
id
}
}
}
}
`,
updateQuery: (previous, { subscriptionData }) => {
// Make vars from the new subscription data
const {
author,
body,
id,
__typename,
createdAt,
replyTo,
} = subscriptionData.data.PostReply.node;
// Clone store
let newPostReplies = _cloneDeep(previous);
// Add sub data to cloned store
newPostReplies.allPostReplies.unshift({
author,
body,
id,
__typename,
createdAt,
replyTo,
});
// Return new store obj
return newPostReplies;
},
});
};
componentDidMount() {
this._subscribeToNewComments();
this._subscribeToNewReplies();
}
render() {
if (this.props.COMMENTS.loading || this.props.COMMENT_REPLIES.loading) {
return <Loading />;
}
const { eventId } = this.props;
const comments = this.props.COMMENTS.allPosts;
const replies = this.props.COMMENT_REPLIES.allPostReplies;
const { user } = this.props.COMMENTS;
const hideNewCommentForm = () => {
if (this.props.hideNewCommentForm === true) return true;
if (!user) return true;
return false;
};
return (
<React.Fragment>
{!hideNewCommentForm() && (
<NewComment
eventId={eventId}
groupOrEvent="event"
queryToUpdate={COMMENTS}
/>
)}
<Comments
comments={comments}
replies={replies}
queryToUpdate={{ COMMENT_REPLIES, eventId }}
hideNewCommentForm={hideNewCommentForm()}
/>
</React.Fragment>
);
}
}
const COMMENTS = gql`
query allPosts($eventId: ID!) {
user {
id
}
allPosts(filter: { event: { id: $eventId } }, orderBy: createdAt_DESC) {
id
body
createdAt
author {
id
}
event {
id
}
}
}
`;
const COMMENT_REPLIES = gql`
query allPostReplies($eventId: ID!) {
allPostReplies(
filter: { replyTo: { event: { id: $eventId } } }
orderBy: createdAt_DESC
) {
id
replyTo {
id
}
body
createdAt
author {
id
}
}
}
`;
const CommentsEventContainerExport = compose(
graphql(COMMENTS, {
name: 'COMMENTS',
}),
graphql(COMMENT_REPLIES, {
name: 'COMMENT_REPLIES',
}),
)(CommentsEventContainer);
export default CommentsEventContainerExport;
这是NewComment组件:
import React from 'react';
import { compose, graphql } from 'react-apollo';
import gql from 'graphql-tag';
import './NewComment.css';
import UserPic from '../UserPic/UserPic';
import Loading from '../Loading/Loading';
class NewComment extends React.Component {
constructor(props) {
super(props);
this.state = {
body: '',
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.onKeyDown = this.onKeyDown.bind(this);
}
handleChange(e) {
this.setState({ body: e.target.value });
}
onKeyDown(e) {
if (e.keyCode === 13) {
e.preventDefault();
this.handleSubmit();
}
}
handleSubmit(e) {
if (e !== undefined) {
e.preventDefault();
}
const { groupOrEvent } = this.props;
const authorId = this.props.USER.user.id;
const { body } = this.state;
const { queryToUpdate } = this.props;
const fakeId = '-' + Math.random().toString();
const fakeTime = new Date();
if (groupOrEvent === 'group') {
const { locationId, groupId } = this.props;
this.props.CREATE_GROUP_COMMENT({
variables: {
locationId,
groupId,
body,
authorId,
},
optimisticResponse: {
__typename: 'Mutation',
createPost: {
__typename: 'Post',
id: fakeId,
body,
createdAt: fakeTime,
reply: null,
event: null,
group: {
__typename: 'Group',
id: groupId,
},
location: {
__typename: 'Location',
id: locationId,
},
author: {
__typename: 'User',
id: authorId,
},
},
},
update: (proxy, { data: { createPost } }) => {
const data = proxy.readQuery({
query: queryToUpdate,
variables: {
groupId,
locationId,
},
});
data.allPosts.unshift(createPost);
proxy.writeQuery({
query: queryToUpdate,
variables: {
groupId,
locationId,
},
data,
});
},
});
} else if (groupOrEvent === 'event') {
const { eventId } = this.props;
this.props.CREATE_EVENT_COMMENT({
variables: {
eventId,
body,
authorId,
},
optimisticResponse: {
__typename: 'Mutation',
createPost: {
__typename: 'Post',
id: fakeId,
body,
createdAt: fakeTime,
reply: null,
event: {
__typename: 'Event',
id: eventId,
},
author: {
__typename: 'User',
id: authorId,
},
},
},
update: (proxy, { data: { createPost } }) => {
const data = proxy.readQuery({
query: queryToUpdate,
variables: { eventId },
});
data.allPosts.unshift(createPost);
proxy.writeQuery({
query: queryToUpdate,
variables: { eventId },
data,
});
},
});
}
this.setState({ body: '' });
}
render() {
if (this.props.USER.loading) return <Loading />;
return (
<form
onSubmit={this.handleSubmit}
className="NewComment NewComment--initial section section--padded"
>
<UserPic userId={this.props.USER.user.id} />
<textarea
value={this.state.body}
onChange={this.handleChange}
onKeyDown={this.onKeyDown}
rows="3"
/>
<button className="btnIcon" type="submit">
Submit
</button>
</form>
);
}
}
const USER = gql`
query USER {
user {
id
}
}
`;
const CREATE_GROUP_COMMENT = gql`
mutation CREATE_GROUP_COMMENT(
$body: String!
$authorId: ID!
$locationId: ID!
$groupId: ID!
) {
createPost(
body: $body
authorId: $authorId
locationId: $locationId
groupId: $groupId
) {
id
body
author {
id
}
createdAt
event {
id
}
group {
id
}
location {
id
}
reply {
id
replyTo {
id
}
}
}
}
`;
const CREATE_EVENT_COMMENT = gql`
mutation CREATE_EVENT_COMMENT($body: String!, $eventId: ID!, $authorId: ID!) {
createPost(body: $body, authorId: $authorId, eventId: $eventId) {
id
body
author {
id
}
createdAt
event {
id
}
}
}
`;
const NewCommentExport = compose(
graphql(CREATE_GROUP_COMMENT, {
name: 'CREATE_GROUP_COMMENT',
}),
graphql(CREATE_EVENT_COMMENT, {
name: 'CREATE_EVENT_COMMENT',
}),
graphql(USER, {
name: 'USER',
}),
)(NewComment);
export default NewCommentExport;
完整的错误消息是:
Warning: Encountered two children with the same key, `cjexujn8hkh5x0192cu27h94k`. Keys should be unique so that components maintain their identity across updates. Non-unique keys may cause children to be duplicated and/or omitted — the behavior is unsupported and could change in a future version.
in ul (at Comments.js:9)
in Comments (at CommentsEventContainer.js:157)
in CommentsEventContainer (created by Apollo(CommentsEventContainer))
in Apollo(CommentsEventContainer) (created by Apollo(Apollo(CommentsEventContainer)))
in Apollo(Apollo(CommentsEventContainer)) (at EventPage.js:110)
in section (at EventPage.js:109)
in DocumentTitle (created by SideEffect(DocumentTitle))
in SideEffect(DocumentTitle) (at EventPage.js:51)
in EventPage (created by Apollo(EventPage))
in Apollo(EventPage) (at App.js:176)
in Route (at App.js:171)
in Switch (at App.js:94)
in div (at App.js:93)
in main (at App.js:80)
in Router (created by BrowserRouter)
in BrowserRouter (at App.js:72)
in App (created by Apollo(App))
in Apollo(App) (at index.js:90)
in QueryRecyclerProvider (created by ApolloProvider)
in ApolloProvider (at index.js:89)
2 回答
这实际上很容易修复 . 我很困惑,因为我的订阅会间歇性地失败 . 事实证明,这是一个Graphcool问题,从亚洲到美国集群的转变阻止了这种脆弱 .
您只需要测试以查看商店中是否已存在ID,如果存在则不添加 . 我添加了代码注释,我已经更改了代码:
我偶然发现了同样的问题,并没有找到一个简单而干净的解决方案 .
我所做的是在服务器上使用订阅解析器的过滤功能 . 您可以按照tutorial来描述如何为客户端设置服务器和tutorial .
简而言之: