UNPKG

reactant-share

Version:

A framework for building shared web applications with Reactant

116 lines (113 loc) 4.58 kB
import { __assign, __read } from './node_modules/tslib/tslib.es6.js'; import { containerKey, identifierKey } from 'reactant'; import { proxyExecutorKey, proxyClientActionName } from './constants.js'; import { PortDetector } from './modules/portDetector.js'; /** * Proxy execute On the server side. * * ## Description * * `delegate()` is very similar to the actor model, * which transfers the corresponding module method to the server thread for execution and returns the result as response. * * Note: It does not create new threads, it always runs on the server thread that has already been created. * * ## Example * * ```tsx * import React from 'react'; * import { ViewModule, createApp, injectable, useConnector, action, state } from 'reactant'; * import { delegate } from 'reactant-share'; * * @injectable({ name: 'counter'}) * class Counter { * @state * count = 0; * * @action * increase() { * this.count += 1; * } * } * * @injectable() * export class AppView extends ViewModule { * constructor(public counter: Counter) { * super(); * } * * component() { * const count = useConnector(() => this.counter.count); * return ( * <button type="button" onClick={() => delegate(this.counter, 'increase', [])}> * {count} * </button> * ); * } * } * ``` * reference: https://en.wikipedia.org/wiki/Actor_model */ var delegate = (function (module, key, args, options) { var _a, _b; if (options === void 0) { options = {}; } var method = module[key]; var _args = args !== null && args !== void 0 ? args : []; if (typeof key !== 'string') { throw new Error("'delegate()' is valid only for method name with string type."); } if (typeof method !== 'function') { throw new Error("The property '".concat(key, "'' must be a method in class '").concat(module.constructor.name, "'.")); } if (!Array.isArray(_args)) { throw new Error("The parameters of the method '".concat(key, "' must be an array.")); } var target = module; if ((_a = target[containerKey]) === null || _a === void 0 ? void 0 : _a.isBound(PortDetector)) { var portDetector_1 = target[containerKey].get(PortDetector); if (process.env.NODE_ENV !== 'production') { var moduleName = target.constructor.name; if (/^@@reactant/.test(target[identifierKey])) { throw new Error("The identifier '".concat(target[identifierKey], "' is a temporary string, please set 'provide' for the module '").concat(moduleName, "' or the 'name' field of the module '").concat(moduleName, "'.")); } } // If the method in main thread and use coworker, it should be proxied for execution to a coworker thread. if (target[proxyExecutorKey]) { return target[proxyExecutorKey]({ module: target[identifierKey], method: key, args: _args, }); } // If the port is not a client, it just run the method in server port. if (portDetector_1.isClient) { if (!portDetector_1.transports.client) { return Promise.reject(new Error("Detected that the current client transport does not exist.")); } return portDetector_1.transports.client .emit(__assign(__assign({}, options), { name: proxyClientActionName }), { module: target[identifierKey], method: key, args: _args, hook: (_b = options._extra) === null || _b === void 0 ? void 0 : _b.serverHook, }) .then(function (response) { // If the response is undefined, it means that the method is not executed. if (!response) return; var _a = __read(response, 2), sequence = _a[0], result = _a[1]; if (portDetector_1.lastAction.sequence >= sequence) { return result; } if (process.env.NODE_ENV !== 'production') { console.warn("The sequence of the action is not consistent in ".concat(target[identifierKey], ".").concat(key, "."), sequence, portDetector_1.lastAction.sequence); } return portDetector_1 .syncFullState({ forceSync: false }) .then(function () { return result; }); }); } } return method.apply(target, _args); }); export { delegate };