UNPKG

@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
'use strict'; 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;