Nun, ich habe meine eigene Lösung gefunden. Ich mutiere die Requisiten nicht, was die Komplikation hinzufügt, an einer veränderlichen Version des Baumes festzuhalten. Dies kann definitiv etwas Reinigung verwenden.
function addPropsIfHOCFactory(predicate) { //
return function addPropsIfHOC(WrappedComponent) { // factory
return (
class FieldLayoutWrap extends WrappedComponent {
render() {
const view = super.render();
if (!this.addProps) return view;
// depth first - stack - last in first out
// iterate depth first until a Field is found
const viewElementNew = { node: view, parentElement: null };
const tree = [viewElementNew]; // stack // parentElement is ref to parentElement in elements
const elementsByDepth = {}; // key is depth, value is array of element's at that depth
const elementsByParentId = {}; // key is elementId of parent
let elementId = 0;
// console.log('view:', view);
let depthMax = 0;
while (tree.length) {
const element = tree.pop();
element.props = element.node.props ? element.node.props : undefined;
element.childrenElements = undefined;
element.clone = undefined;
element.depth = getDepth(element);
element.id = elementId++;
element.needsClone = false; // if true then clone, its set to true if props are changed
if (element.depth > depthMax) depthMax = element.depth;
if (!elementsByDepth[element.depth]) {
elementsByDepth[element.depth] = [];
}
elementsByDepth[element.depth].push(element);
const node = element.node;
const primative = typeof node;
if (primative === 'object' && node) {
if (predicate(node)) {
const addProps = isFunction(this.addProps) ? this.addProps(node) : this.addProps;
element.props = Object.assign({}, node.props ? node.props : undefined, addProps);
markBranchNeedsClone(element);
console.log('added props to node:', element.node);
}
}
if (node.props && node.props.children) {
const children = node.props.children;
const pushChild = child => {
const parent = element;
const childElement = {
node: child,
parentElement: parent
}
tree.push(childElement);
if (!elementsByParentId[parent.id]) elementsByParentId[parent.id] = [];
elementsByParentId[parent.id].push(childElement);
return childElement;
}
if (Array.isArray(children)) {
element.childrenElements = children.map(pushChild);
} else {
const child = children;
element.childrenElements = pushChild(child);
}
}
}
// do React.cloneElement from deepest first IF needsClone === true
let depth = depthMax + 1;
while (depth--) {
// console.log('doing now elementsByDepth[depth] of depth:', depth);
for (const element of elementsByDepth[depth]) {
if (typeof element.node === 'object' && element.node) {
if (!element.needsClone) {
element.clone = element.node;
} else {
let childrenClones = elementsByParentId[element.id];
if (childrenClones) {
if (childrenClones.length === 1) {
childrenClones = childrenClones[0].clone;
} else {
childrenClones = childrenClones.map(element => element.clone);
}
}
console.log('CLONING');
element.clone = React.cloneElement(element.node, element.props, childrenClones);
}
} else {
// its a string, number etc
element.clone = element.node;
}
}
}
// console.log('viewElementNew:', viewElementNew);
// console.log('elementsByDepth:', elementsByDepth);
// console.log('elementsByParentId:', elementsByParentId);
return viewElementNew.clone;
}
}
)
}
}
function isFunction(functionToCheck) {
var getType = {};
return functionToCheck && getType.toString.call(functionToCheck) === '[object Function]';
}
function getDepth(element) {
let depth = 0;
let elementCur = element;
while (elementCur.parentElement) {
elementCur = elementCur.parentElement;
depth++;
}
return depth;
}
function markBranchNeedsClone(element) {
let elementCur = element;
while (elementCur) {
elementCur.needsClone = true;
elementCur = elementCur.parentElement;
}
}
Verbrauch:
import React from 'react'
import ReactDOM from 'react-dom'
import addPropsIfHOC from 'add-props-if'
// MY FORM COMPONENT
class BlahDumb extends React.Component {
addProps = {
placeholder: 'INJECTED PLACEHOLDER'
}
render() {
return (
<form>
<label htmlFor="url">URL</label>
<div>
<input id="url" type="text" />
yeppers
</div>
<div>
<input id="foo" type="text" />
</div>
</form>
)
}
}
const BlahPropsAdded = addPropsIfHOC(node => node.type === 'input')
const Blah = BlahPropsAdded(BlahDumb);
// MY APP COMPONENT
class App extends React.PureComponent {
render() {
return (
<div className="app">
<Blah />
</div>
)
}
}
// RENDER
ReactDOM.render(<App />, document.getElementById('app'))
Hier arbeitet - https://codesandbox.io/s/6y1lrn7yww
Hier eine Demo, die Requisiten zu <Field>
Kinder ergänzt: https://codesandbox.io/s/9zp9207nvy
Es kann ein Weg, dies zu erreichen Arbeit, aber sicherlich React-Dokumentation empfiehlt die Verwendung von [Zusammensetzung statt Vererbung] (https://facebook.github.io/react/docs/composition-vs-inheritance.html). Anstatt eine vorhandene Klasse zu erweitern, können Sie möglicherweise das erreichen, was Sie möchten, indem Sie eins in das andere einfügen. Die äußere Komponente kann eine Logik haben, um die Requisiten zu ändern, die an das Kind übergeben werden. – aherriot