(function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('mobx'), require('react')) :
    typeof define === 'function' && define.amd ? define(['exports', 'mobx', 'react'], factory) :
    (global = global || self, factory(global.mobxReactLite = {}, global.mobx, global.React));
}(this, function (exports, mobx, React) { 'use strict';

    var React__default = 'default' in React ? React['default'] : React;

    if (!React.useState) {
        throw new Error("mobx-react-lite requires React with Hooks support");
    }
    if (!mobx.spy) {
        throw new Error("mobx-react-lite requires mobx at least version 4 to be available");
    }

    function useObservable(initialValue) {
        var observableRef = React.useRef(null);
        if (!observableRef.current) {
            observableRef.current = mobx.observable(initialValue);
        }
        return observableRef.current;
    }

    function useComputed(func, inputs) {
        if (inputs === void 0) { inputs = []; }
        var computed = React.useMemo(function () { return mobx.computed(func); }, inputs);
        return computed.get();
    }

    var doNothingDisposer = function () {
        // empty
    };
    /**
     * Adds an observable effect (reaction, autorun, or anything else that returns a disposer) that will be registered upon component creation and disposed upon unmounting.
     * Returns the generated disposer for early disposal.
     *
     * @export
     * @template D
     * @param {() => D} disposerGenerator A function that returns the disposer of the wanted effect.
     * @param {ReadonlyArray<any>} [inputs=[]] If you want the effect to be automatically re-created when some variable(s) are changed then pass them in this array.
     * @returns {D}
     */
    function useDisposable(disposerGenerator, inputs) {
        if (inputs === void 0) { inputs = []; }
        var disposerRef = React.useRef(null);
        var earlyDisposedRef = React.useRef(false);
        React.useEffect(function () {
            return lazyCreateDisposer(false);
        }, inputs);
        function lazyCreateDisposer(earlyDisposal) {
            // ensure that we won't create a new disposer if it was early disposed
            if (earlyDisposedRef.current) {
                return doNothingDisposer;
            }
            if (!disposerRef.current) {
                var newDisposer = disposerGenerator();
                if (typeof newDisposer !== "function") {
                    var error = new Error("generated disposer must be a function");
                    {
                        // tslint:disable-next-line:no-console
                        console.error(error);
                        return doNothingDisposer;
                    }
                }
                disposerRef.current = newDisposer;
            }
            return function () {
                if (disposerRef.current) {
                    disposerRef.current();
                    disposerRef.current = null;
                }
                if (earlyDisposal) {
                    earlyDisposedRef.current = true;
                }
            };
        }
        return lazyCreateDisposer(true);
    }

    var globalIsUsingStaticRendering = false;
    function useStaticRendering(enable) {
        globalIsUsingStaticRendering = enable;
    }
    function isUsingStaticRendering() {
        return globalIsUsingStaticRendering;
    }

    /*! *****************************************************************************
    Copyright (c) Microsoft Corporation. All rights reserved.
    Licensed under the Apache License, Version 2.0 (the "License"); you may not use
    this file except in compliance with the License. You may obtain a copy of the
    License at http://www.apache.org/licenses/LICENSE-2.0

    THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
    WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
    MERCHANTABLITY OR NON-INFRINGEMENT.

    See the Apache Version 2.0 License for specific language governing permissions
    and limitations under the License.
    ***************************************************************************** */

    var __assign = function() {
        __assign = Object.assign || function __assign(t) {
            for (var s, i = 1, n = arguments.length; i < n; i++) {
                s = arguments[i];
                for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
            }
            return t;
        };
        return __assign.apply(this, arguments);
    };

    function __read(o, n) {
        var m = typeof Symbol === "function" && o[Symbol.iterator];
        if (!m) return o;
        var i = m.call(o), r, ar = [], e;
        try {
            while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
        }
        catch (error) { e = { error: error }; }
        finally {
            try {
                if (r && !r.done && (m = i["return"])) m.call(i);
            }
            finally { if (e) throw e.error; }
        }
        return ar;
    }

    function printDebugValue(v) {
        if (!v.current) {
            return "<unknown>";
        }
        return mobx.getDependencyTree(v.current);
    }

    var EMPTY_ARRAY = [];
    function useUnmount(fn) {
        React.useEffect(function () { return fn; }, EMPTY_ARRAY);
    }
    function useForceUpdate() {
        var _a = __read(React.useState(0), 2), setTick = _a[1];
        var update = React.useCallback(function () {
            setTick(function (tick) { return tick + 1; });
        }, []);
        return update;
    }
    function isPlainObject(value) {
        if (!value || typeof value !== "object") {
            return false;
        }
        var proto = Object.getPrototypeOf(value);
        return !proto || proto === Object.prototype;
    }

    var EMPTY_OBJECT = {};
    function useObserver(fn, baseComponentName, options) {
        if (baseComponentName === void 0) { baseComponentName = "observed"; }
        if (options === void 0) { options = EMPTY_OBJECT; }
        if (isUsingStaticRendering()) {
            return fn();
        }
        var wantedForceUpdateHook = options.useForceUpdate || useForceUpdate;
        var forceUpdate = wantedForceUpdateHook();
        var reaction = React.useRef(null);
        if (!reaction.current) {
            reaction.current = new mobx.Reaction("observer(" + baseComponentName + ")", function () {
                forceUpdate();
            });
        }
        var dispose = function () {
            if (reaction.current && !reaction.current.isDisposed) {
                reaction.current.dispose();
            }
        };
        React.useDebugValue(reaction, printDebugValue);
        useUnmount(function () {
            dispose();
        });
        // render the original component, but have the
        // reaction track the observables, so that rendering
        // can be invalidated (see above) once a dependency changes
        var rendering;
        var exception;
        reaction.current.track(function () {
            try {
                rendering = fn();
            }
            catch (e) {
                exception = e;
            }
        });
        if (exception) {
            dispose();
            throw exception; // re-throw any exceptions catched during rendering
        }
        return rendering;
    }

    // n.b. base case is not used for actual typings or exported in the typing files
    function observer(baseComponent, options) {
        // The working of observer is explained step by step in this talk: https://www.youtube.com/watch?v=cPF4iBedoF0&feature=youtu.be&t=1307
        if (isUsingStaticRendering()) {
            return baseComponent;
        }
        var realOptions = __assign({ forwardRef: false }, options);
        var baseComponentName = baseComponent.displayName || baseComponent.name;
        var wrappedComponent = function (props, ref) {
            return useObserver(function () { return baseComponent(props, ref); }, baseComponentName);
        };
        wrappedComponent.displayName = baseComponentName;
        // memo; we are not intested in deep updates
        // in props; we assume that if deep objects are changed,
        // this is in observables, which would have been tracked anyway
        var memoComponent;
        if (realOptions.forwardRef) {
            // we have to use forwardRef here because:
            // 1. it cannot go before memo, only after it
            // 2. forwardRef converts the function into an actual component, so we can't let the baseComponent do it
            //    since it wouldn't be a callable function anymore
            memoComponent = React.memo(React.forwardRef(wrappedComponent));
        }
        else {
            memoComponent = React.memo(wrappedComponent);
        }
        copyStaticProperties(baseComponent, memoComponent);
        memoComponent.displayName = baseComponentName;
        return memoComponent;
    }
    // based on https://github.com/mridgway/hoist-non-react-statics/blob/master/src/index.js
    var hoistBlackList = {
        $$typeof: true,
        render: true,
        compare: true,
        type: true
    };
    function copyStaticProperties(base, target) {
        Object.keys(base).forEach(function (key) {
            if (base.hasOwnProperty(key) && !hoistBlackList[key]) {
                Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(base, key));
            }
        });
    }

    function ObserverComponent(_a) {
        var children = _a.children, render = _a.render;
        var component = children || render;
        if (typeof component !== "function") {
            return null;
        }
        return useObserver(component);
    }
    ObserverComponent.propTypes = {
        children: ObserverPropsCheck,
        render: ObserverPropsCheck
    };
    ObserverComponent.displayName = "Observer";
    function ObserverPropsCheck(props, key, componentName, location, propFullName) {
        var extraKey = key === "children" ? "render" : "children";
        var hasProp = typeof props[key] === "function";
        var hasExtraProp = typeof props[extraKey] === "function";
        if (hasProp && hasExtraProp) {
            return new Error("MobX Observer: Do not use children and render in the same time in`" + componentName);
        }
        if (hasProp || hasExtraProp) {
            return null;
        }
        return new Error("Invalid prop `" +
            propFullName +
            "` of type `" +
            typeof props[key] +
            "` supplied to" +
            " `" +
            componentName +
            "`, expected `function`.");
    }

    function useAsObservableSourceInternal(current, usedByLocalStore) {
        if (usedByLocalStore && current === undefined) {
            return undefined;
        }
        var _a = __read(React__default.useState(function () { return mobx.observable(current, {}, { deep: false }); }), 1), res = _a[0];
        mobx.runInAction(function () {
            Object.assign(res, current);
        });
        return res;
    }
    function useAsObservableSource(current) {
        return useAsObservableSourceInternal(current, false);
    }

    function useLocalStore(initializer, current) {
        var source = useAsObservableSourceInternal(current, true);
        return React__default.useState(function () {
            var local = mobx.observable(initializer(source));
            if (isPlainObject(local)) {
                mobx.runInAction(function () {
                    Object.keys(local).forEach(function (key) {
                        var value = local[key];
                        if (typeof value === "function") {
                            local[key] = wrapInTransaction(value, local);
                        }
                    });
                });
            }
            return local;
        })[0];
    }
    // tslint:disable-next-line: ban-types
    function wrapInTransaction(fn, context) {
        return function () {
            var args = [];
            for (var _i = 0; _i < arguments.length; _i++) {
                args[_i] = arguments[_i];
            }
            return mobx.transaction(function () { return fn.apply(context, args); });
        };
    }

    exports.Observer = ObserverComponent;
    exports.isUsingStaticRendering = isUsingStaticRendering;
    exports.observer = observer;
    exports.useAsObservableSource = useAsObservableSource;
    exports.useComputed = useComputed;
    exports.useDisposable = useDisposable;
    exports.useForceUpdate = useForceUpdate;
    exports.useLocalStore = useLocalStore;
    exports.useObservable = useObservable;
    exports.useObserver = useObserver;
    exports.useStaticRendering = useStaticRendering;

    Object.defineProperty(exports, '__esModule', { value: true });

}));
