【发布时间】:2021-05-11 19:31:46
【问题描述】:
我在一节课中看到我们可以使用组合 api 挂钩 usePromise 创建,但问题是我有一个带有待办事项列表的简单 crud 应用程序,我在其中创建、删除、获取 API 调用,但我不明白我是如何可以将此钩子用于一个组件中的所有 api。所有调用都正确,但加载不正确,它仅在第一次调用 PostService.getAll() 时才起作用,然后加载程序没有被触发。感谢您的回复。
usePromise.js
import { ref } from 'vue';
export default function usePromise(fn) {
const results = ref(null);
const error = ref(null);
const loading = ref(false);
const createPromise = async (...args) => {
loading.value = true;
error.value = null;
results.value = null;
try {
results.value = await fn(...args);
} catch (err) {
error.value = err;
} finally {
loading.value = false;
}
};
return { results, loading, error, createPromise };
}
apiClient.js
import axios from 'axios';
export default axios.create({
baseURL: 'https://jsonplaceholder.typicode.com/',
withCredentials: false,
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
});
PostService.js
import apiClient from './apiClient';
const urlPath = '/posts';
export default {
getAll() {
return apiClient.get(urlPath);
},
add(post) {
return apiClient.post(urlPath, post);
},
delete(id) {
return apiClient.delete(`${urlPath}/${id}`);
},
};
List.vue
<template>
<div>
<VLoader v-if="loading" />
<template v-else>
<table class="table">
<thead>
<tr>
<th>Id</th>
<th>Title</th>
<th></th>
</tr>
</thead>
<tbody>
<tr v-for="post in posts" :key="post.id">
<td>{{ post.id }}</td>
<td>{{ post.title }}</td>
<td>
<button class="btn btn-danger ml-1" @click="deletePost(post.id)">Delete</button>
</td>
</tr>
</tbody>
</table>
</template>
</div>
</template>
<script>
import { ref, computed, watch, unref } from 'vue';
import PostService from '@/services/PostService';
import usePromise from '@/use/usePromise';
export default {
setup() {
const posts = ref([]);
const post = ref({
title: '',
body: '',
});
const {
results: postsResultRef,
loading: postsLoadingRef,
createPromise: getAllPosts,
} = usePromise(() => PostService.getAll());
getAllPosts(); //get all posts by initialize component
const {
results: postDeleteResultRef,
loading: postDeleteLoadingRef,
createPromise: deletePost,
} = usePromise((id) => PostService.delete(id).then((result) => ({ ...result, removedId: id })));
watch(postsResultRef, (postsResult) => {
posts.value = postsResult.data;
});
watch(postDeleteResultRef, (postDeleteResult) => {
if (postDeleteResult.status === 200) {
posts.value = posts.value.filter((item) => item.id != postDeleteResult.removeId);
// unref(posts).splice(/* remove postDeleteResult.removedId */);
}
});
const loading = computed(() => [postsLoadingRef, postDeleteLoadingRef].map(unref).some(Boolean));
return { posts, post, loading };
},
};
</script>
【问题讨论】:
-
钩子被误用了。应该在初始化设置时调用钩子,而不是在 deletePost 等内部。
-
但是如果我们在设置中多次调用这个钩子会很混乱,当它在我认为它更具可读性的方法中时,不是吗?
-
重点不在于可读性,而在于正确使用钩子。那样使用它是不正确的,它不是为这样使用而设计的。它使用可以绑定在模板中并与计算值结合的 refs。如果您仍然到处都有
createPromise().then(() =>...,则用钩子包裹承诺不会受益,您可以在不使用引用的情况下在then中获得相同的结果。
标签: vue.js vuejs3 vue-composition-api