Repository URL to install this package:
|
Version:
1.2.12 ▾
|
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.reactTreeWalker = reactTreeWalker;
var _promiseFun = require("../../_forks/promise-fun");
var _deps = require("./deps");
/* eslint-disable no-console */
// Inspired by the awesome work by the Apollo team: 😘
// https://github.com/apollographql/react-apollo/blob/master/src/getDataFromTree.ts
//
// This version has been adapted to be Promise based and support native Preact.
// Recurse a React Element tree, running the provided visitor against each element.
// If a visitor call returns `false` then we will not recurse into the respective
// elements children.
function reactTreeWalker(tree, visitor, context, options = _deps.defaultOptions) {
return new Promise((resolve, reject) => {
const safeVisitor = (...args) => {
try {
return visitor(...args);
} catch (err) {
reject(err);
}
return undefined;
};
const recursive = (currentElement, currentContext) => {
if (Array.isArray(currentElement)) {
return Promise.all(currentElement.map(item => recursive(item, currentContext)));
}
if (!currentElement) {
return Promise.resolve();
}
if (typeof currentElement === 'string' || typeof currentElement === 'number') {
// Just visit these, they are leaves so we don't keep traversing.
safeVisitor(currentElement, null, currentContext);
return Promise.resolve();
}
if ((0, _deps.isReactElement)(currentElement)) {
return new Promise(innerResolve => {
const visitCurrentElement = (render, compInstance, elContext, childContext) => Promise.resolve(safeVisitor(currentElement, compInstance, elContext, childContext)).then(result => {
if (result !== false) {
// A false wasn't returned so we will attempt to visit the children
// for the current element.
const tempChildren = render();
const children = (0, _deps.ensureChild)(tempChildren);
if (children) {
if (Array.isArray(children)) {
// If its a react Children collection we need to breadth-first
// traverse each of them, and pMapSeries allows us to do a
// depth-first traversal that respects Promises. Thanks @sindresorhus!
return (0, _promiseFun.pMapSeries)(children, child => child ? recursive(child, childContext) : Promise.resolve()).then(innerResolve, reject).catch(reject);
} // Otherwise we pass the individual child to the next recursion.
return recursive(children, childContext).then(innerResolve, reject).catch(reject);
}
}
return undefined;
}).catch(reject);
if (typeof (0, _deps.getType)(currentElement) === 'function') {
const Component = (0, _deps.getType)(currentElement);
const props = Object.assign({}, Component.defaultProps, (0, _deps.getProps)(currentElement), // For Preact support so that the props get passed into render
// function.
{
children: (0, _deps.getChildren)(currentElement)
});
if ((0, _deps.isClassComponent)(Component)) {
// Class component
const instance = new Component(props, currentContext); // In case the user doesn't pass these to super in the constructor
instance.props = instance.props || props;
instance.context = instance.context || currentContext; // set the instance state to null (not undefined) if not set, to match React behaviour
instance.state = instance.state || null; // Make the setState synchronous.
instance.setState = newState => {
if (typeof newState === 'function') {
// eslint-disable-next-line no-param-reassign
newState = newState(instance.state, instance.props, instance.context);
}
instance.state = Object.assign({}, instance.state, newState);
};
if (instance.componentWillMount) {
instance.componentWillMount();
}
const childContext = (0, _deps.providesChildContext)(instance) ? Object.assign({}, currentContext, instance.getChildContext()) : currentContext;
visitCurrentElement( // Note: preact API also allows props and state to be referenced
// as arguments to the render func, so we pass them through
// here
() => instance.render(instance.props, instance.state), instance, currentContext, childContext).then(() => {
if (options.componentWillUnmount && instance.componentWillUnmount) {
instance.componentWillUnmount();
}
}).then(innerResolve);
} else {
// Stateless Functional Component
visitCurrentElement(() => Component(props, currentContext), null, currentContext, currentContext).then(innerResolve);
}
} else {
// A basic element, such as a dom node, string, number etc.
visitCurrentElement(() => (0, _deps.getChildren)(currentElement), null, currentContext, currentContext).then(innerResolve);
}
});
} // Portals
if (currentElement.containerInfo && currentElement.children && currentElement.children.props && Array.isArray(currentElement.children.props.children)) {
return Promise.all(currentElement.children.props.children.map(child => recursive(child, currentContext)));
}
return Promise.resolve();
};
recursive(tree, context).then(resolve, reject);
});
}