use-sse
Version:
useSSE - use server-side effect
134 lines (133 loc) • 4.73 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createServerContext = exports.createBroswerContext = exports.useSSE = exports.InternalContext = exports.DataContext = void 0;
const react_1 = __importDefault(require("react"));
exports.DataContext = react_1.default.createContext({});
exports.InternalContext = react_1.default.createContext({
requests: [],
resolved: false,
current: 0,
});
const react_2 = require("react");
/**
*
* @param effect runction returning promise
* @param dependencies list of dependencies like in useEffect
*/
function useSSE(effect, dependencies) {
var _a, _b;
const internalContext = react_2.useContext(exports.InternalContext);
let callId = internalContext.current;
internalContext.current++;
const ctx = react_2.useContext(exports.DataContext);
const [data, setData] = react_2.useState(((_a = ctx[callId]) === null || _a === void 0 ? void 0 : _a.data) || null);
const [error, setError] = react_2.useState(((_b = ctx[callId]) === null || _b === void 0 ? void 0 : _b.error) || null);
if (!internalContext.resolved) {
let cancel = Function.prototype;
const effectPr = new Promise((resolve) => {
cancel = () => {
if (!ctx[callId]) {
ctx[callId] = { error: { messgae: "timeout" }, id: callId };
}
resolve(callId);
};
return effect()
.then((res) => {
return res;
})
.then((res) => {
ctx[callId] = { data: res };
resolve(callId);
})
.catch((error) => {
ctx[callId] = { error: error };
resolve(callId);
});
});
internalContext.requests.push({
id: callId,
promise: effectPr,
cancel: cancel,
});
}
react_2.useEffect(() => {
if (internalContext.resolved && !ctx[callId]) {
effect()
.then((res) => {
setData(res);
})
.catch((error) => {
setError(error);
});
}
delete ctx[callId];
}, dependencies);
return [data, error];
}
exports.useSSE = useSSE;
const createBroswerContext = (variableName = "_initialDataContext") => {
const initial = window && window[variableName] ? window[variableName] : {};
let internalContextValue = {
current: 0,
resolved: true,
requests: [],
};
function BroswerDataContext(props) {
return (react_1.default.createElement(exports.InternalContext.Provider, { value: internalContextValue },
react_1.default.createElement(exports.DataContext.Provider, { value: initial }, props.children)));
}
return BroswerDataContext;
};
exports.createBroswerContext = createBroswerContext;
const wait = (time) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject({ error: "timeout" });
}, time);
});
};
const createServerContext = () => {
let ctx = {};
let internalContextValue = {
current: 0,
resolved: false,
requests: [],
};
function ServerDataContext(props) {
return (react_1.default.createElement(exports.InternalContext.Provider, { value: internalContextValue },
react_1.default.createElement(exports.DataContext.Provider, { value: ctx }, props.children)));
}
const resolveData = async (timeout) => {
const effects = internalContextValue.requests.map((item) => item.promise);
if (timeout) {
const timeOutPr = wait(timeout);
await Promise.all(internalContextValue.requests.map((effect, index) => {
return Promise.race([effect.promise, timeOutPr]).catch(() => {
return effect.cancel();
});
}));
}
else {
await Promise.all(effects);
}
internalContextValue.resolved = true;
internalContextValue.current = 0;
return {
data: ctx,
toJSON: function () {
return this.data;
},
toHtml: function (variableName = "_initialDataContext") {
return `<script>window.${variableName} = ${JSON.stringify(this)};</script>`;
},
};
};
return {
ServerDataContext,
resolveData,
};
};
exports.createServerContext = createServerContext;