我正在尝试编写一个HOC,如果它是某种类型,则添加prop
。我首先深入研究它。但是当我尝试设置道具它说它不可扩展时,我试图将value
的道具添加到HEEHAW
:
function fieldLayoutHOC(WrappedComponent: ComponentType) {
return (
class FieldLayoutWrap extends WrappedComponent {
static displayName = wrapDisplayName(WrappedComponent, 'FieldLayoutWrap')
render() {
const view = super.render()
// depth first - stack - last in first out
// iterate depth first until a Field is found
const elements = [view]; // stack
console.log('view:', view);
while (elements.length) {
const element = elements.pop();
const primative = typeof element;
if (primative === 'object') {
if (element.type === Field) {
// fields.push(element);
element.props.value = 'HEEHAW'; /////// not extensible error here
console.log('added value HEEHAWW');
} else {
if (element.props) {
const children = element.props.children;
if (children) {
if (Array.isArray(children)) {
elements.push(...children);
} else {
elements.push(children);
}
}
}
}
}
}
return view;
}
}
)
}
我做错了吗?
答案 0 :(得分:1)
我想出了自己的解决方案。我没有改变道具,这增加了坚持树的可变版本的复杂性。这肯定可以使用一些清洁。
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;
}
}
用法:
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'))
这里有效 - https://codesandbox.io/s/6y1lrn7yww
这是一个向<Field>
孩子添加道具的演示:https://codesandbox.io/s/9zp9207nvy