UNPKG

next-unified-query

Version:

React hooks and components for next-unified-query-core

356 lines (351 loc) 11.5 kB
"use client"; 'use strict'; var React = require('react'); var compat = require('es-toolkit/compat'); var nextUnifiedQueryCore = require('next-unified-query-core'); function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; } var React__default = /*#__PURE__*/_interopDefault(React); // src/hooks/use-query.ts var QueryClientContext = React.createContext(null); function HydrationBoundary({ state, children }) { const client = useQueryClient(); const hydratedRef = React.useRef(false); if (state && !hydratedRef.current) { client.hydrate(state); hydratedRef.current = true; } return /* @__PURE__ */ React__default.default.createElement(React__default.default.Fragment, null, children); } function QueryClientProvider({ client, options, children }) { const queryClient = client || nextUnifiedQueryCore.getQueryClient(options); return /* @__PURE__ */ React__default.default.createElement(QueryClientContext.Provider, { value: queryClient }, children); } function useQueryClient() { const ctx = React.useContext(QueryClientContext); if (!ctx) throw new Error("You must wrap your component tree with <QueryClientProvider>."); return ctx; } function useQuery(arg1, arg2) { if (compat.isObject(arg1) && compat.has(arg1, "cacheKey") && compat.isFunction(arg1.cacheKey)) { const query = arg1; nextUnifiedQueryCore.validateQueryConfig(query); const options = arg2 ?? {}; const params = options.params; const cacheKey = query.cacheKey?.(params); const url = query.url?.(params); const queryFn = query.queryFn; const schema = query.schema; const placeholderData = options.placeholderData ?? query.placeholderData; const fetchConfig = options.fetchConfig ?? query.fetchConfig; const select = options.select ?? query.select; const selectDeps = options.selectDeps ?? query.selectDeps; const enabled = compat.has(options, "enabled") ? options.enabled : compat.isFunction(query.enabled) ? query.enabled(params) : query.enabled; return _useQueryObserver({ ...query, ...options, enabled, cacheKey, url, queryFn, params, schema, placeholderData, fetchConfig, select, selectDeps }); } return _useQueryObserver({ ...arg1 }); } function _useQueryObserver(options) { nextUnifiedQueryCore.validateQueryConfig(options); const queryClient = useQueryClient(); const observerRef = React.useRef(void 0); const defaultResultRef = React.useRef({ data: void 0, error: void 0, isLoading: true, isFetching: true, isError: false, isSuccess: false, isStale: true, isPlaceholderData: false, refetch: () => { } }); if (!observerRef.current) { observerRef.current = new nextUnifiedQueryCore.QueryObserver(queryClient, { ...options, key: options.cacheKey }); } else { observerRef.current.setOptions({ ...options, key: options.cacheKey }); } const subscribe = React.useCallback((callback) => { return observerRef.current.subscribe(callback); }, []); const getSnapshot = React.useCallback(() => { if (!observerRef.current) { return defaultResultRef.current; } return observerRef.current.getCurrentResult(); }, []); const result = React.useSyncExternalStore( subscribe, getSnapshot, getSnapshot // getServerSnapshot도 동일하게 ); React.useEffect(() => { observerRef.current?.start(); }, []); React.useEffect(() => { return () => { observerRef.current?.destroy(); }; }, []); return result; } var getInitialState = () => ({ data: void 0, error: null, isPending: false, isSuccess: false, isError: false }); function useMutation(configOrOptions, overrideOptions) { const isFactoryConfig = "url" in configOrOptions || "mutationFn" in configOrOptions; if (isFactoryConfig && overrideOptions) { const factoryConfig = configOrOptions; const mergedOptions = mergeMutationOptions(factoryConfig, overrideOptions); return _useMutationInternal(mergedOptions); } else { return _useMutationInternal(configOrOptions); } } function mergeMutationOptions(factoryConfig, overrideOptions) { const factoryOnMutate = factoryConfig.onMutate; const factoryOnSuccess = factoryConfig.onSuccess; const factoryOnError = factoryConfig.onError; const factoryOnSettled = factoryConfig.onSettled; const overrideOnMutate = overrideOptions.onMutate; const overrideOnSuccess = overrideOptions.onSuccess; const overrideOnError = overrideOptions.onError; const overrideOnSettled = overrideOptions.onSettled; return { // Factory 기본 속성들 ...factoryConfig, // Override 옵션들로 덮어쓰기 (콜백 제외) ...overrideOptions, // 콜백들은 양쪽 모두 실행하도록 병합 onMutate: combinedCallback(factoryOnMutate, overrideOnMutate), onSuccess: combinedCallback(factoryOnSuccess, overrideOnSuccess), onError: combinedCallback(factoryOnError, overrideOnError), onSettled: combinedCallback(factoryOnSettled, overrideOnSettled) }; } function combinedCallback(first, second) { if (!first && !second) return void 0; if (!first) return second; if (!second) return first; return (...args) => { const firstResult = first(...args); const secondResult = second(...args); if (firstResult && typeof firstResult.then === "function") { return firstResult.then(() => secondResult); } return secondResult; }; } function _useMutationInternal(options) { const queryClient = useQueryClient(); const fetcher = queryClient.getFetcher(); if (process.env.NODE_ENV !== "production") { try { nextUnifiedQueryCore.validateMutationConfig(options); } catch (error) { throw error; } } const [state, dispatch] = React.useReducer( (prevState, action) => { switch (action.type) { case "MUTATE": return { ...prevState, isPending: true, isSuccess: false, isError: false, error: null }; case "SUCCESS": return { ...prevState, isPending: false, isSuccess: true, isError: false, data: action.data, error: null }; case "ERROR": return { ...prevState, isPending: false, isSuccess: false, isError: true, error: action.error }; case "RESET": return getInitialState(); default: return prevState; } }, getInitialState() ); const latestOptions = React.useRef(options); latestOptions.current = options; const getMutationFn = React.useCallback(() => { if ("mutationFn" in options && options.mutationFn) { return options.mutationFn; } return async (variables, fetcher2) => { const urlBasedOptions = options; const url = compat.isFunction(urlBasedOptions.url) ? urlBasedOptions.url(variables) : urlBasedOptions.url; const method = urlBasedOptions.method; let dataForRequest = variables; if (options.requestSchema) { try { dataForRequest = options.requestSchema.parse(variables); } catch (e) { if (e instanceof nextUnifiedQueryCore.z.ZodError) { const config = { url, method, data: variables }; const fetchError = new nextUnifiedQueryCore.FetchError( `Request validation failed: ${e.issues.map((issue) => issue.message).join(", ")}`, config, "ERR_VALIDATION" ); fetchError.name = "ValidationError"; fetchError.cause = e; fetchError.isValidationError = true; throw fetchError; } throw e; } } const requestConfig = compat.merge( { schema: options.responseSchema }, // fetcher.defaults에서 baseURL을 가져와서 기본값으로 설정 { baseURL: fetcher2.defaults.baseURL }, options.fetchConfig || {}, { url, method, data: dataForRequest } ); const response = await fetcher2.request(requestConfig); return response.data; }; }, [options, fetcher]); const mutateCallback = React.useCallback( async (variables, mutateLocalOptions) => { dispatch({ type: "MUTATE", variables }); let context; try { const onMutateCb = latestOptions.current.onMutate; if (onMutateCb) { context = await onMutateCb(variables); } const mutationFn = getMutationFn(); const data = await mutationFn(variables, fetcher); dispatch({ type: "SUCCESS", data }); if (latestOptions.current.onSuccess) { await latestOptions.current.onSuccess(data, variables, context); } if (mutateLocalOptions?.onSuccess) { mutateLocalOptions.onSuccess(data, variables, context); } const invalidateQueriesOption = latestOptions.current.invalidateQueries; if (invalidateQueriesOption) { let keysToInvalidate; if (compat.isFunction(invalidateQueriesOption)) { keysToInvalidate = invalidateQueriesOption(data, variables, context); } else { keysToInvalidate = invalidateQueriesOption; } if (compat.isArray(keysToInvalidate)) { keysToInvalidate.forEach((queryKey) => { queryClient.invalidateQueries(queryKey); }); } } if (latestOptions.current.onSettled) { await latestOptions.current.onSettled(data, null, variables, context); } if (mutateLocalOptions?.onSettled) { mutateLocalOptions.onSettled(data, null, variables, context); } return data; } catch (err) { const error = err; dispatch({ type: "ERROR", error }); if (latestOptions.current.onError) { await latestOptions.current.onError(error, variables, context); } if (mutateLocalOptions?.onError) { mutateLocalOptions.onError(error, variables, context); } if (latestOptions.current.onSettled) { await latestOptions.current.onSettled(void 0, error, variables, context); } if (mutateLocalOptions?.onSettled) { mutateLocalOptions.onSettled(void 0, error, variables, context); } throw error; } }, [getMutationFn, queryClient, fetcher] ); const mutate = React.useCallback( (variables, localOptions) => { mutateCallback(variables, localOptions).catch(() => { }); }, [mutateCallback] ); const mutateAsync = React.useCallback( (variables, localOptions) => { return mutateCallback(variables, localOptions); }, [mutateCallback] ); const reset = React.useCallback(() => { dispatch({ type: "RESET" }); }, []); return { ...state, mutate, mutateAsync, reset }; } exports.HydrationBoundary = HydrationBoundary; exports.QueryClientProvider = QueryClientProvider; exports.useMutation = useMutation; exports.useQuery = useQuery; exports.useQueryClient = useQueryClient; //# sourceMappingURL=react.js.map //# sourceMappingURL=react.js.map