UNPKG

@ewb/reach-react

Version:
181 lines (180 loc) 7.7 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()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.useCrud = void 0; const react_1 = require("react"); const reach_1 = require("@ewb/reach"); const ReachContext_1 = require("./ReachContext"); function useCrud(path, data, props) { const service = react_1.useContext(ReachContext_1.ReachContext); const reach = react_1.useMemo(() => new reach_1.Reach(service), [service]); const init = react_1.useRef(false); const initialData = react_1.useMemo(() => JSON.parse(JSON.stringify(data)), [data]); const defaultState = react_1.useMemo(() => (Object.assign(Object.assign({}, getNewState(path, initialData)), { dirty: !initialData[props.idKey], busy: Boolean(props.initWithGet && initialData[props.idKey]) })), [path, props.idKey, initialData]); const ref = react_1.useRef(defaultState); const queue = react_1.useRef([]); const [state, setState] = react_1.useState(defaultState); const id = react_1.useMemo(() => state.data[props.idKey], [state.data, props.idKey]); const opts = react_1.useMemo(() => props.reachOptions || {}, [props.reachOptions]); const fetch = react_1.useCallback((method) => () => __awaiter(this, void 0, void 0, function* () { try { if (!id) { console.warn('Fetch used when id is undefined'); return; } const apiPath = `${path}/${id}`; const data = yield reach.api(apiPath, Object.assign(Object.assign({}, opts), { method })); ref.current = getNewState(path, Object.assign(Object.assign({}, ref.current.data), data)); setState(ref.current); } catch (error) { ref.current.busy = false; ref.current.error = error; setState((s) => (Object.assign(Object.assign({}, s), { busy: false, error }))); } }), [reach, opts, path, id]); const patch = react_1.useCallback((state, key) => __awaiter(this, void 0, void 0, function* () { try { const id = state.data[props.idKey]; if (id && !Object.values(state.edited).some(Boolean)) { return null; } if (ref.current.busy) { queue.current.push(Object.assign({}, state)); return ref.current.data; } if (!ref.current.busy) { ref.current.busy = true; setState((s) => (Object.assign(Object.assign({}, s), { busy: true }))); } let data; let apiPath = `${path}${id ? `/${id}` : ''}`; if (props.subPath) { apiPath += `/${props.subPath}`; } if (!props.alwaysPost && (id || props.alwaysPatch)) { const body = getPatchData(state, props.forcePatch, key); data = yield reach.api(apiPath, Object.assign(Object.assign({}, opts), { method: 'PATCH', body })); } else { data = yield reach.api(apiPath, Object.assign(Object.assign({}, opts), { method: 'POST', body: ref.current.data })); } if (queue.current.length > 0) { const patchState = queue.current[0]; queue.current.splice(0, 1); ref.current.busy = false; yield patch(patchState); } else if (!props.dontSetStateOnPost) { let edited = {}; if (key) { edited = ref.current.edited; edited[key] = false; } ref.current = getNewState(path, data || ref.current.data, edited, ref.current.meta); setState(ref.current); } return data; } catch (error) { setState((s) => (Object.assign(Object.assign({}, s), { busy: false, error }))); ref.current.busy = false; ref.current.error = error; return null; } }), [ reach, path, props.idKey, props.subPath, opts, props.dontSetStateOnPost, props.alwaysPatch, props.alwaysPost, props.forcePatch, ]); const set = react_1.useCallback((key, disableAutoSave = props.disableAutoSave) => (event, meta) => { const value = event && typeof event === 'object' && 'target' in event ? event.target.value : event; setState((s) => { const edited = Object.assign(Object.assign({}, s.edited), { [key]: !s.initialData[key] || s.initialData[key] !== value }); ref.current = Object.assign(Object.assign({}, s), { edited, dirty: Object.values(edited).some(Boolean), data: Object.assign(Object.assign({}, s.data), { [key]: value }) }); if (meta) { ref.current.meta[key] = meta; } if (!disableAutoSave) { patch(ref.current, key); } return ref.current; }); }, [props.disableAutoSave, patch]); const save = react_1.useCallback(() => patch(ref.current), [patch]); const setData = react_1.useCallback((data, meta = {}, isEdited = true) => { setState((s) => { const edited = Object.assign({}, s.edited); // @ts-ignore Object.keys(data).forEach((key) => { if (s.data[key] !== data[key]) { edited[key] = isEdited; } }); ref.current = getNewState(s.path, Object.assign(Object.assign({}, s.data), data), edited, Object.assign(Object.assign({}, s.meta), meta)); return ref.current; }); }, []); const actions = react_1.useMemo(() => ({ read: fetch('GET'), delete: fetch('DELETE') }), [fetch]); react_1.useEffect(() => { if (!init.current && props.initWithGet && id) { actions.read(); } init.current = true; }, [props.initWithGet, id, actions.read]); return react_1.useMemo(() => { const ret = [state, set, save, setData, actions, setState]; ret.state = state; ret.set = set; ret.save = save; ret.setData = setData; ret.actions = actions; ret.setState = setState; return ret; }, [state, set, save, setData, actions, setState]); } exports.useCrud = useCrud; function getPatchData(state, forcePatch, key) { if (key) { return { [key]: state.data[key], }; } const patchData = {}; for (const key in state.edited) { if (state.edited[key]) { patchData[key] = state.data[key]; } } if (forcePatch) { for (const key of forcePatch) { patchData[key] = state.data[key]; } } return patchData; } function getNewState(path, data, edited = {}, meta = {}) { return { path, busy: false, dirty: Object.values(edited).some(Boolean), data, initialData: data, edited, meta, }; }