UNPKG

@gechiui/components

Version:
125 lines (107 loc) 4.28 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.useComponentsContext = exports.ContextSystemProvider = exports.ComponentsContext = void 0; var _element = require("@gechiui/element"); var _lodash = require("lodash"); var _warning = _interopRequireDefault(require("@gechiui/warning")); /** * External dependencies */ /** * GeChiUI dependencies */ const ComponentsContext = (0, _element.createContext)( /** @type {Record<string, any>} */ {}); exports.ComponentsContext = ComponentsContext; const useComponentsContext = () => (0, _element.useContext)(ComponentsContext); /** * Runs an effect only on update (i.e., ignores the first render) * * @param {import('react').EffectCallback} effect * @param {import('react').DependencyList} deps */ exports.useComponentsContext = useComponentsContext; function useUpdateEffect(effect, deps) { const mounted = (0, _element.useRef)(false); (0, _element.useEffect)(() => { if (mounted.current) { return effect(); } mounted.current = true; return undefined; }, deps); } /** * Consolidates incoming ContextSystem values with a (potential) parent ContextSystem value. * * Note: This function will warn if it detects an un-memoized `value` * * @param {Object} props * @param {Record<string, any>} props.value * @return {Record<string, any>} The consolidated value. */ function useContextSystemBridge(_ref) { let { value } = _ref; const parentContext = useComponentsContext(); const valueRef = (0, _element.useRef)(value); useUpdateEffect(() => { if ( // objects are equivalent (0, _lodash.isEqual)(valueRef.current, value) && // but not the same reference valueRef.current !== value) { typeof process !== "undefined" && process.env && process.env.NODE_ENV !== "production" ? (0, _warning.default)(`Please memoize your context: ${JSON.stringify(value)}`) : void 0; } }, [value]); // `parentContext` will always be memoized (i.e., the result of this hook itself) // or the default value from when the `ComponentsContext` was originally // initialized (which will never change, it's a static variable) // so this memoization will prevent `merge` and `cloneDeep` from rerunning unless // the references to `value` change OR the `parentContext` has an actual material change // (because again, it's guaranteed to be memoized or a static reference to the empty object // so we know that the only changes for `parentContext` are material ones... i.e., why we // don't have to warn in the `useUpdateEffect` hook above for `parentContext` and we only // need to bother with the `value`). The `useUpdateEffect` above will ensure that we are // correctly warning when the `value` isn't being properly memoized. All of that to say // that this should be super safe to assume that `useMemo` will only run on actual // changes to the two dependencies, therefore saving us calls to `merge` and `cloneDeep`! const config = (0, _element.useMemo)(() => { return (0, _lodash.merge)((0, _lodash.cloneDeep)(parentContext), value); }, [parentContext, value]); return config; } /** * A Provider component that can modify props for connected components within * the Context system. * * @example * ```jsx * <ContextSystemProvider value={{ Button: { size: 'small' }}}> * <Button>...</Button> * </ContextSystemProvider> * ``` * * @template {Record<string, any>} T * @param {Object} options * @param {import('react').ReactNode} options.children Children to render. * @param {T} options.value Props to render into connected components. * @return {JSX.Element} A Provider wrapped component. */ const BaseContextSystemProvider = _ref2 => { let { children, value } = _ref2; const contextValue = useContextSystemBridge({ value }); return (0, _element.createElement)(ComponentsContext.Provider, { value: contextValue }, children); }; const ContextSystemProvider = (0, _element.memo)(BaseContextSystemProvider); exports.ContextSystemProvider = ContextSystemProvider; //# sourceMappingURL=context-system-provider.js.map