UNPKG

@ewb/reach-react

Version:
154 lines (153 loc) 7.82 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.useSearch = void 0; const reach_1 = require("@ewb/reach"); const react_1 = require("react"); const ReachContext_1 = require("./ReachContext"); const SKIP = 0; const LIMIT = 10; const COUNT = 0; const LIMIT_KEY = 'limit'; const SKIP_KEY = 'skip'; const COUNT_HEADER = 'X-Total-Count'; const toInitialState = (props) => ({ busy: !props.disableInit, limit: props.limit || LIMIT, skip: props.skip || props.defaultSkip || SKIP, page: 0, count: props.count || COUNT, items: props.defaultItems || [], searchQuery: props.query || {}, hasFetched: false, }); const toInitialQuery = (state, skipKey, limitKey = LIMIT_KEY) => (Object.assign(Object.assign({}, state.searchQuery), { [limitKey]: state.limit, [skipKey]: state.skip })); function useSearch(path, props) { const { skipKey = SKIP_KEY, countHeader = COUNT_HEADER, responseToData, reachOptions, skipPages, paginationMode = 'client', } = props; const init = react_1.useRef(false); const service = react_1.useContext(ReachContext_1.ReachContext); const initialState = react_1.useMemo(() => toInitialState(props), [props]); const [state, setState] = react_1.useState(initialState); const _page = react_1.useRef(state.skip); const initialQuery = react_1.useRef(toInitialQuery(state, skipKey, props.limitKey)); const searchQuery = react_1.useRef(initialQuery.current); const reach = react_1.useMemo(() => new reach_1.Reach(service), [service]); const search = react_1.useCallback((page, reachQuery) => __awaiter(this, void 0, void 0, function* () { try { const paginate = page !== 0; _page.current = page; if (page === 0) { searchQuery.current = Object.assign(Object.assign({}, initialQuery.current), (reachQuery || {})); } const querySkip = skipPages ? page : page * state.limit; const query = reachQuery ? Object.assign(Object.assign(Object.assign({}, initialQuery.current), reachQuery), { [skipKey]: querySkip }) : Object.assign(Object.assign({}, initialQuery.current), { [skipKey]: querySkip }); const response = yield reach.api(path, Object.assign(Object.assign({}, reachOptions), { query, noJson: true })); const json = (yield response.json()); const newState = getNewStateFromResponse(response, json, querySkip, query, countHeader); const toNewItems = (s, items) => items ? (paginate && paginationMode === 'client' ? [...s.items, ...items] : items) : s.items; if (typeof responseToData === 'function') { let retItems = []; setState((s) => { const _a = responseToData(json, Object.assign(Object.assign({}, s), newState), response, paginate), { items } = _a, responseState = __rest(_a, ["items"]); if (items) { retItems = items; } return Object.assign(Object.assign(Object.assign(Object.assign({}, s), newState), responseState), { items: toNewItems(s, items) }); }); return retItems; } if (!Array.isArray(json)) { throw new Error('useSearch error. json response is not typeof array. Use responseToData to parse response'); } setState((s) => (Object.assign(Object.assign(Object.assign({}, s), newState), { items: toNewItems(s, json) }))); return json; } catch (error) { setState((s) => (Object.assign(Object.assign({}, s), { busy: false, error }))); return []; } }), [path, responseToData, reachOptions, skipKey, countHeader, state.limit, skipPages, paginationMode]); const next = react_1.useCallback((searchQuery, page = _page.current + 1) => __awaiter(this, void 0, void 0, function* () { if (state.items.length < state.count) { setState((s) => (Object.assign(Object.assign({}, s), { busy: true }))); return search(page, searchQuery); } return null; }), [state.items.length, state.count, search]); const info = react_1.useMemo(() => ({ limit: state.limit, skip: state.skip, page: _page.current, count: state.count, hasFetched: state.hasFetched, json: state.json, }), [state.limit, state.skip, state.count, state.hasFetched, state.json]); const actions = react_1.useMemo(() => ({ unshift: (...items) => { setState((s) => (Object.assign(Object.assign({}, s), { items: [...items, ...s.items], count: s.count + items.length }))); }, splice: (start, deleteCount = 1, ...items) => { setState((s) => { const newItems = [...s.items]; newItems.splice(start, deleteCount, ...items); return Object.assign(Object.assign({}, s), { items: newItems, count: s.count - deleteCount + items.length }); }); }, push: (...items) => { setState((s) => { return Object.assign(Object.assign({}, s), { items: [...s.items, ...items], count: s.count + items.length }); }); }, search: (query) => { _page.current = 0; return search(props.defaultSkip || 0, query); }, map: (fn) => setState((s) => (Object.assign(Object.assign({}, s), { items: s.items.map(fn) }))), filter: (fn) => setState((s) => (Object.assign(Object.assign({}, s), { items: s.items.filter(fn) }))), }), [search, props.defaultSkip]); react_1.useEffect(() => { if (!init.current && !props.disableInit) { search(0).then(); } init.current = true; }, [search, props.disableInit]); return react_1.useMemo(() => [state.busy, state.items, state.error, next, info, actions], [state.busy, state.items, state.error, next, info, actions]); } exports.useSearch = useSearch; function getNewStateFromResponse(response, json, skip, searchQuery, countHeader) { const newState = { skip, searchQuery, busy: false, hasFetched: true, error: null, json, }; if (countHeader) { const count = Number(response.headers.get(countHeader)); if (count && !isNaN(count)) { newState.count = count; } } return newState; }