Как реализовать компоновку SSR Friendly с общим состоянием в Nuxt3?

В моем приложении есть функция поиска. Он вызывает 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 
    };
};

Мой вопрос:

Я делаю это совершенно неправильно? Мне просто нужна функция поиска с глобальным состоянием, чтобы получить к ней доступ из разных компонентов. Один задает критерий поиска, другой выполняет поиск, а третий отображает результаты. Состояние загрузки можно использовать для включения/отключения кнопки поиска. Как я могу добиться этого, дружественным к реформе безопасности?

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
51
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

зачем использовать «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')

Не рекомендую github.com/nuxt/nuxt/issues/21772 👀

Tim 13.08.2024 12:23

ок, давай попробуем:

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 загрузка и дедупликация поведения

Другие вопросы по теме