HOC如果是某种类型则添加道具

时间:2017-08-18 23:38:16

标签: reactjs

我正在尝试编写一个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;
            }
        }
    )
}

我做错了吗?

1 个答案:

答案 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