В моем приложении есть функция поиска. Он вызывает Rest API и возвращает результаты. Чтобы избежать двойных вызовов , я использую useAsyncData:
const { data, status, error, execute } = await useAsyncData('item', () => $fetch('https://dummyjson.com/users/search?q=John'));
Теперь я хочу использовать эту функцию поиска в нескольких компонентах моего приложения, например. поиск на панели навигации и страница поиска. Чтобы избежать дублирования кода, я поместил весь код, связанный с поиском, в новый составной объект: useSearch.
export default () => {
const toSearch = ref('John');
const fetchResponse = async () => useAsyncData('item', () => $fetch('https://dummyjson.com/users/search?q=' + toSearch));
return {
toSearch,
fetchResponse,
};
};
который я затем могу использовать в своих компонентах следующим образом:
const { toSearch, fetchResults } = useSearch();
toSearch.value = 'john';
const { data, status, error, execute } = await fetchResults();
Это работает, но возникает несколько проблем, когда я хочу иметь только одно глобальное состояние с текущими результатами поиска и состоянием загрузки. До сих пор, если два компонента используют этот составной объект, каждый получит свой собственный экземпляр этого составного объекта (согласно замыслу).
Чтобы поделиться состоянием, я использовал useState:
const toSearch = useState(() => 'John');
Теперь каждый экземпляр этого составного объекта будет иметь один и тот же поисковый запрос. Но мне также нужны результаты, ожидающие рассмотрения и статус, которыми можно поделиться. Я не могу этого сделать, потому что не могу получить доступ к тем, что находятся внутри моего компонуемого объекта. Эти значения возвращаются только после того, как откуда-то вызывается useAsyncData. Единственное, к чему я могу получить доступ, — это результаты внутри крючка преобразования:
export default () => {
const toSearch = useState(() => 'John');
const results = useState(() => undefined);
const fetchResponse = async () => useAsyncData('item', () => $fetch('https://dummyjson.com/users/search?q=' + toSearch), {
transform: (res) => {
results.value = res;
}
} );
return {
toSearch,
fetchResponse,
results
};
};
Мой вопрос:
Я делаю это совершенно неправильно? Мне просто нужна функция поиска с глобальным состоянием, чтобы получить к ней доступ из разных компонентов. Один задает критерий поиска, другой выполняет поиск, а третий отображает результаты. Состояние загрузки можно использовать для включения/отключения кнопки поиска. Как я могу добиться этого, дружественным к реформе безопасности?
зачем использовать «toSearch» в качестве ссылки и не делать что-то вроде
export default async (searchParam) => {
const fetchResponse = await useAsyncData('item', () => $fetch('https://dummyjson.com/users/search?q=' + searchParam));
return {
fetchResponse,
}};
и делать что-то подобное?
const { fetchResults } = await useSearch('search-values');
или вы также можете получить доступ с помощью nuxtData
await useSearch('search-value')
const data = useNuxtData('item')
ок, давай попробуем:
export default (searchParam) => {
const fetchResponse = $fetch('https://dummyjson.com/users/search?q=' +
searchParam));
return fetchResponse,
};
а потом:
const { data, status } = await useAsyncData('item', () => useSearch('search-values'));
Вы можете инициализировать плагин с состоянием и просто обновить API.
export default defineNuxtPlugin(async (nuxtApp) => {
const query = useState('search-query', () => "");
const data = useState('search-data');
const status = useState('search-status');
const error = useState('search-error');
const res = useAsyncData('..', () =>
$fetch('....', {
query: { q: query.value },
immediate: false, // this will prevent wasted API call.
}),
{ watch: [query] }
);
watchEffect(() => {
data.value = res.data.value;
status.value = res.status.value;
error.value = res.error.value;
});
});
Теперь используйте составной объект, чтобы просто раскрыть состояние.
export function useSearch() {
const query = useState('search-query');
const data = useState('search-data');
const status = useState('search-status');
const error = useState('search-error');
return { query, data, status, error };
}
я использую плагин $api для обработки всех значений по умолчанию
// plugins/api.ts
export default defineNuxtPlugin(() => {
const config = useRuntimeConfig()
const $api = $fetch.create({
baseURL: config.public.apiBase ?? '',
onResponse({ response }) {
},
onRequest({ options, request }) {
},
async onResponseError(ctx) {
},
})
return {
provide: {
api: $api,
},
}
})
тогда я использую собственный useFetch
// composables/apiFetch.ts
export function useApiFetch<TRes, TData = TRes>(url: MaybeRefOrGetter<string>, options: UseFetchOptions<TRes, TData> = {}) {
const defaults: UseFetchOptions<TRes, TData> = {
deep: false,
$fetch: useNuxtApp().$api,
}
// for nice deep defaults, please use unjs/defu
const params = defu(options, defaults)
return useFetch(url, params)
}
а затем я использую компонуемые элементы для каждого запроса, чтобы иметь контролируемое поведение с каждым запросом:
export function useApiClientGetList<T extends ApiClientGetListParams>(params: MaybeRefOrGetter<T> = {} as T, options: UseApiFetchOptions<ApiResponsePaginated<Client>> = {}) {
const notOverridableOptions: UseFetchOptions<ApiResponsePaginated<Client>> = { method: 'GET', params }
return useApiFetch('/clients', defu(notOverridableOptions, options))
}
тогда я назову это useFetch-like
const { data: clientData, status, refresh: fetchClients } = useApiClientGetList(clientsParams, { immediate: false })
поэтому загрузка - это просто useFetches загрузка и дедупликация поведения
Не рекомендую github.com/nuxt/nuxt/issues/21772 👀