@gechiui/components
Version:
UI components for GeChiUI.
125 lines (107 loc) • 4.28 kB
JavaScript
;
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