react-to-imperative
Version:
extract props from React elements
80 lines (79 loc) • 2.28 kB
JavaScript
import React from 'react';
export const inspectElements = (inputReactElements, opts) => {
const {
propsExtractor,
maxDepth = 3,
elementCreator = callSafe
} = typeof opts === 'function' ? {
propsExtractor: opts
} : opts;
const extractedProps = extractFromElement(inputReactElements, {
maxDepth,
elementCreator
}, propsExtractor);
return extractedProps;
};
export default inspectElements;
const extractFromElement = (elementRoot, opts, propsExtractor) => {
// don't do this at home - it's not how React is meant to be used
const stack = [{
element: elementRoot,
depth: 0
}];
const results = [];
while (stack.length > 0) {
const {
element,
depth
} = stack.shift();
React.Children.forEach(element, child => {
if (! /*#__PURE__*/React.isValidElement(child)) {
return;
}
const result = propsExtractor({
type: child.type,
props: child.props,
depth
});
const nextDepth = depth + 1;
const isGettingTooDeep = nextDepth >= opts.maxDepth;
if (typeof result === 'object') {
results.push(result);
} else if (result === true && !isGettingTooDeep) {
// go one level deeper
const {
props,
type
} = child;
if (typeof type === 'function') {
const nestedElement = opts.elementCreator(type, props);
stack.push({
element: nestedElement,
depth: nextDepth
});
} else {
// Fragment or other element
const children = child.props.children;
if (children) {
stack.push({
element: children,
depth: nextDepth
});
}
}
}
});
}
return results;
};
export const callSafe = (type, props) => {
try {
// if the component is a pure function component, call it directly with props and return the result
// @ts-expect-error Not all constituents of type 'JSXElementConstructor<I>' are callable.
return type(props);
} catch {
// catches errors like "Invalid hook call" if you use hooks or "cannot call a class as a function" if you use class components
return null;
}
};
//# sourceMappingURL=index.js.map