通过参数化 getter 计算的 Vue 数组不是反应性的



我有一个列表,一个包含评论数组的讨论对象,每个评论可以保存一个回复数组。我以这种方式显示讨论:

<div v-for="comment in comments" v-bind:key="comment._id">
<Comment :itemId="itemId" :comment="comment" />
<Replies v-if="comment.replies.length > 0" :itemId="itemId" :comment="comment" />
</div>
<Button value="Load more" @clicked="loadMoreComments(itemId)" />

和回复:

<div v-for="reply in replies" v-bind:key="reply._id">
<Comment :itemId="itemId" :comment="reply" />
</div>
<Button :value="Load more" @clicked="loadChild()"/>

如您所见,两者都使用相同的模式。它们在计算属性中有所不同:

computed: {
comments() {
return this.$store.getters.DISCUSSION.comments.map(id => this.$store.getters.GET_COMMENT(id));
},
replies() {
return this.$store.getters.GET_REPLIES(this.comment).map(id => this.$store.getters.GET_COMMENT(id));
},
},

当我点击"加载更多"按钮以获取评论时,会出现新评论。但是当我在回复中点击"加载更多"按钮时,尽管我可以在调试器中看到数组已放大,但没有显示新回复。

Vuex 存储子模块:

state: () => ({
discussion: {
incomplete: true,
comments: [],
},
comments: {},
}),
getters: {
DISCUSSION: state => state.discussion,
GET_COMMENT: state => id => state.comments[id],
GET_REPLIES: state => (comment) => {
if (comment.allShown) {
return comment.replies;
}
return comment.replies.slice(0, REPLY_LIMIT);
},
},
mutations: {
APPEND_COMMENTS: (state, payload) => {
const { comments, incomplete, userId } = payload;
state.discussion.incomplete = incomplete;
const commentIds = [];
comments.forEach(comment => processComment(state, comment, commentIds, userId));
state.discussion.comments = state.discussion.comments.concat(commentIds);
},
PREPEND_COMMENTS: (state, payload) => {
const { comments, userId } = payload;
const commentIds = [];
comments.forEach(comment => processComment(state, comment, commentIds, userId));
state.discussion.comments = commentIds.concat(state.discussion.comments);
},
SET_REPLIES: (state, payload) => {
console.log('SET_REPLIES');
const { commentId, replies, userId, replace } = payload;
const comment = state.comments[commentId];
if (!comment) {
return;
}
state.comments[commentId].showAll = true;
const commentIds = [];
replies.forEach(reply => processComment(state, reply, commentIds, userId));
if (!comment.replies || comment.replies.length === 0 || replace) {
state.comments[commentId].replies = commentIds;
} else {
state.comments[commentId].replies = comment.replies.concat(commentIds);
}
},
}
function processComment(state, comment, commentIds, userId) {
if (comment.replies) {
const repliesIds = [];
comment.replies.forEach((reply) => {
reply.voted = hasVoted(reply.votes, userId);
state.comments[reply._id] = reply;
repliesIds.push(reply._id);
});
comment.replies = repliesIds;
comment.allShown = comment.replies.length < REPLY_LIMIT;
} else if (!comment.parentId) {
comment.replies = [];
comment.allShown = false;
}
state.comments[comment._id] = comment;
commentIds.push(comment._id);
}

完整的源代码在那里:https://github.com/literakl/mezinamiridici/tree/comment_refactoring/spa

以下是最小可重现代码沙箱: https://codesandbox.io/s/frosty-taussig-v8u4b?file=/src/module.js

我已经验证了这是因为带有参数的 getter 而发生的。当我将回复放在静态数组中以便我可以使用无参数 getter 时,它开始工作。

我遵循以下建议:https://forum.vuejs.org/t/vuex-best-practices-for-complex-objects/10143

问题出在哪里?

更新:

闻起来的一件事是突变GET_REPLIES因为它适用于传递的对象。所以 Vue 没有机会检测到对象来自状态。所以我重写了它以仅传递 commentId 并从状态加载注释,但它没有帮助。

我的客人应该用allShownprop 替换showAll,并使用Vue.set向对象添加新键comments因为由于 Vue 警告 Vuex 看不到新的道具,请参阅对象的注意事项

SET_REPLIES: (state, payload) => {
console.log("SET_REPLIES");
const { commentId, replies, userId, replace } = payload;
const comment = state.comments[commentId];
if (!comment) {
console.log(`Comment ${commentId} not found`);
return;
}
state.comments[commentId].allShown = true;
// state.comments[commentId].showAll = true;
...
function processComment(state, comment, commentIds, userId) {
if (comment.replies) {
const repliesIds = [];
comment.replies.forEach(reply => {
Vue.set(state.comments, reply._id, reply);
// state.comments[reply._id] = reply;
repliesIds.push(reply._id);
});
comment.replies = repliesIds;
comment.allShown = comment.replies.length < 3;
} else if (!comment.parentId) {
comment.replies = [];
comment.allShown = false;
}
Vue.set(state.comments, comment._id, comment);
// state.comments[comment._id] = comment;
commentIds.push(comment._id);
}

还要更正GET_REPLIES这样的调用:

computed: {
replies() {
return this.$store.getters
.GET_REPLIES(this.comment) // passing comment itself instead of its id
.map(id => this.$store.getters.GET_COMMENT(id));
}
},

更正的示例

最新更新