@shopify/app-bridge-host
Version:
App Bridge Host contains components and middleware to be consumed by the app's host, as well as the host itself. The middleware and `Frame` component are responsible for facilitating communication between the client and host, and used to act on actions se
153 lines (146 loc) • 6.55 kB
JavaScript
;
Object.defineProperty(exports, '__esModule', { value: true });
var tslib = require('tslib');
var React = require('react');
var reactRedux = require('react-redux');
var Error = require('@shopify/app-bridge-core/actions/Error');
var Middleware = require('./Middleware.js');
var store_index = require('./store/index.js');
var actions = require('./store/reducers/embeddedApp/appBridge/actions.js');
var Host = require('./Host.js');
var api = require('./api.js');
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
var React__default = /*#__PURE__*/_interopDefault(React);
/**
* The context provider for the app, config and addReducer method
* @public
* */
var HostContext = React.createContext(null);
/**
* The context provider for the router.
* Keeps track of the current location and
* handles history push/replace
* @public
* */
var RouterContext = React.createContext(null);
/**
* A component that creates a dynamic Redux store
* and renders the Host
* @public
* */
function HostProvider(props) {
var _a = React.useState(false), initialized = _a[0], setInitialized = _a[1];
var config = props.config, configureApi = props.configureApi, debug = props.debug, dispatchClientEventHandler = props.dispatchClientEventHandler, initialState = props.initialState, middleware = props.middleware, router = props.router, children = props.children, errorHandler = props.errorHandler, hostProps = tslib.__rest(props, ["config", "configureApi", "debug", "dispatchClientEventHandler", "initialState", "middleware", "router", "children", "errorHandler"]);
var apiKey = config && config.apiKey;
var previousApiKey = usePrevious(apiKey);
var apiKeyChanged = (!previousApiKey && apiKey !== undefined) || apiKey !== previousApiKey;
var previousInitialState = usePrevious(JSON.stringify(initialState));
var initialStateChanged = JSON.stringify(initialState) !== previousInitialState;
var _b = React.useState(), addReducer = _b[0], setAddReducer = _b[1];
var _c = React.useState(), app = _c[0], setApp = _c[1];
var dispatchClientEventHandlerRef = React.useRef(dispatchClientEventHandler);
dispatchClientEventHandlerRef.current = dispatchClientEventHandler;
var appBridgeMiddleware = React.useMemo(function () { return Middleware.buildMiddleware(store_index.APP_BRIDGE_KEY, dispatchClientEventHandlerRef, errorHandler); }, []);
React.useEffect(function () {
if (!appBridgeMiddleware || typeof appBridgeMiddleware.load !== 'function') {
Error.throwError(Error.AppActionType.MISSING_APP_BRIDGE_MIDDLEWARE, 'Missing required context `appBridgeMiddleware`. Maybe you forgot the App Bridge `<Provider>` component?');
}
}, [appBridgeMiddleware]);
var storeCreated = React.useRef(false);
var store = React.useMemo(function () {
if (storeCreated.current) {
Error.throwError(Error.AppActionType.REDUX_REINSTANTIATED, 'Store cannot be instantiated twice. Redux middleware has changed or debug mode was toggled.');
}
storeCreated.current = true;
var hostMiddleware = middleware
? middleware.concat([appBridgeMiddleware])
: [appBridgeMiddleware];
// Only take the features state as features is the only default reducer
var defaultState = initialState.features && { features: initialState.features };
return store_index.createStore(hostMiddleware, defaultState, debug);
}, [appBridgeMiddleware, debug, middleware]);
React.useEffect(function () {
if (initialized) {
return;
}
store.dispatch(actions.StoreReadyAction);
setInitialized(true);
}, [initialized, store]);
React.useEffect(function () {
if (!config || !apiKeyChanged) {
return;
}
var newApp = appBridgeMiddleware.load({
config: config,
type: 'application',
});
setApp(newApp);
}, [appBridgeMiddleware, config, apiKeyChanged]);
React.useEffect(function () {
if (!initialStateChanged) {
return;
}
setAddReducer(function () { return store_index.createAddReducer(store, initialState); });
}, [store, initialState, initialStateChanged]);
var onRouteChangeListeners = React.useRef(new Set());
var addRouteChangeListener = React.useCallback(function (onRouteChangeListener) {
onRouteChangeListeners.current.add(onRouteChangeListener);
return function () {
onRouteChangeListeners.current.delete(onRouteChangeListener);
};
}, []);
var notifyRouteChange = React.useCallback(function (options) {
onRouteChangeListeners.current.forEach(function (onRouteChangeListener) {
onRouteChangeListener(options);
});
}, []);
var hostContext = React.useMemo(function () {
if (!config || !app || !addReducer) {
return;
}
var context = {
app: app,
addReducer: addReducer,
config: config,
store: store,
addRouteChangeListener: addRouteChangeListener,
notifyRouteChange: notifyRouteChange,
errorHandler: errorHandler,
};
if (!configureApi) {
return context;
}
return tslib.__assign(tslib.__assign({}, context), { api: api.createApiContext() });
}, [
app,
addReducer,
config,
configureApi,
store,
addRouteChangeListener,
notifyRouteChange,
errorHandler,
]);
if (!hostContext) {
// eslint-disable-next-line react/no-children-prop
return React__default.default.createElement(reactRedux.Provider, { store: store, children: undefined });
}
var hostContent = (React__default.default.createElement(HostContext.Provider, { value: hostContext },
React__default.default.createElement(reactRedux.Provider, { store: store },
React__default.default.createElement(Host.default, tslib.__assign({}, hostProps)),
children)));
if (!router) {
return hostContent;
}
return React__default.default.createElement(RouterContext.Provider, { value: router }, hostContent);
}
function usePrevious(value) {
var ref = React.useRef();
React.useEffect(function () {
ref.current = value;
}, [value]);
return ref.current;
}
exports.HostContext = HostContext;
exports.RouterContext = RouterContext;
exports.default = HostProvider;