在运行时动态包装随机数React组件

时间:2017-09-17 17:11:17

标签: javascript reactjs

如果我说了以下简单的组件:

const CompOne = (props) => {
  return (
    <div className="compOne">
      {props.children}
    </div>
  );
};

const CompTwo = (props) => {
  return (
    <div className="compTwo">
      {props.children}
    </div>
  );
};

const CompThree = (props) => {
  return (
    <div className="compThree">
      {props.content}
    </div>
  );
};

现在,在运行时,在发出AJAX请求之后,客户端会收到信息,这些信息给出了组件需要相互包装的顺序。这个AJAX请求的结果看起来像这样:

let renderMap = [
  { component: CompOne, props: {} },
  { component: CompTwo, props: {} },
  { component: CompThree, props: { content: "hi there" } }
];

因此,组合应该通过遍历数组并将一个组件组合到下一个组件中来进行流动。例如:CompOne(CompTwo(CompThree)))

当我尝试创建包装HOC来解决此问题时需要注意的两件重要事项:

编辑:我在原帖中忘记提及的重要细节

1)要包装的组件数量不一致。有时它可能是3,但其他时间需要多达4,5,6个组件相互包裹

2)订单每次都可能不同

<CompOne>
  <CompTwo>
    <CompThree content="hi there">
    </CompThree>
  </CompTwo>
</CompOne>

所以我得到的HTML将是:

<div className="compOne">
  <div className="compTwo">
    <div className="compThree">
      hi there
    </div>
  </div>
</div>

我已经尝试了各种各样的东西,但是一旦我开始过去只包装两个组件,我就无法工作。这是我甚至可以在React做的事吗?

2 个答案:

答案 0 :(得分:2)

与评论中显示的http://social.itsfewster.co.uk/#services链接一样,您可以使用存储在变量中的组件 - 使用JSX - 只要它们大写:

// in render()
const MyComp = props.someVariableContainingAComponent;
return <MyComp />;

考虑到这一点,你的问题的一种方法是迭代所有组件,从内部组件开始,然后将下一个组件用作前一个组件的包装器。给定测试数据renderMap的形状,并使用Array.protype.reduce进行迭代,它看起来像这样:

renderComponents(renderMap) {
  const Component = renderMap
   .reverse()
   .reduce( (ComponentSoFar, {component, props}) => {
     const Outer = component;
     return () => (<Outer {...props} ><ComponentSoFar /></Outer>);
   }, props => null ); // initial value, just a "blank" component
 return ( <Component /> );
}

我已经包含了一个演示,演示了如何使用这种方法处理不同数量的组件和不同的嵌套顺序。

&#13;
&#13;
const CompOne = (props) => (
    <div className="comp compOne"><p>One:</p>{ props.content || props.children } </div>);
const CompTwo = (props) => (
	<div className="comp compTwo"><p>Two:</p> { props.content || props.children }</div>);
const CompThree = (props) => (
	<div className="comp compThree"><p>Three:</p> { props.content || props.children }</div>);
const CompFour = (props) => (
	<div className="comp compFour"><p>Four:</p> { props.content || props.children }</div>);
const CompFive = (props) => (
	<div className="comp compFive"><p>Five:</p> { props.content || props.children }</div>);

const renderMap1 = [
  { component: CompOne, props: {} },
  { component: CompTwo, props: {} },
  { component: CompThree, props: {} },
  { component: CompFour, props: {} },
  { component: CompFive, props: { content: "hi there" } }
];
const renderMap2 = [].concat(renderMap1.slice(1,4).reverse(), renderMap1.slice(4))
const renderMap3 = renderMap2.slice(1);

class App extends React.Component {
  constructor(props) {
    super(props);
  }
  
  renderComponents(renderMap) {
     const Component = renderMap
      .reverse()
      .reduce( (ComponentSoFar, {component, props}) => {
        const Outer = component;
        return () => (<Outer {...props} ><ComponentSoFar /></Outer>);
      }, props => null ); // initial value, just a "blank" component
    return ( <Component /> );
  }
  
  render() {
    return ( <div> 
      { this.renderComponents(renderMap1) } 
      { this.renderComponents(renderMap2) } 
      { this.renderComponents(renderMap3) } 
    </div> );
  }
};
ReactDOM.render(<App />, document.getElementById('root'));
&#13;
.comp {
  border: 5px solid green;
  padding: 5px;
  margin: 5px;
  display: inline-block;
}
.compOne { border-color: red;}
.compTwo { border-color: green;}
.compThree { border-color: blue;}
.compFour { border-color: black;}
.compFive { border-color: teal;}
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

<div id="root"></div>
&#13;
&#13;
&#13;

答案 1 :(得分:1)

修改:问题中添加了新信息。鉴于这些信息,这种方法不起作用。

您可以使用Higher-order component (HOC)解决此问题,如下所示:

const superWrapped = (Outer) => (Middle) => (Inner) => props => {
   return (
    <Outer>
      <Middle>
        <Inner content={props.content} />
      </Middle>
    </Outer>
   )
 };

以后你会像这样使用它:

render() {
  const SuperWrapped = 
    superWrapped(CompOne)(CompThree)(CompTwo); // any order is fine!
  return (<SuperWrapped content="Something here.." /> );
}

为了实现这一点,您需要对组件进行一些小的调整。我在下面提供了一个工作演示:

&#13;
&#13;
const superWrapped = (Outer) => (Middle) => (Inner) => props => {
	return (
    <Outer>
      <Middle>
        <Inner content={props.content} />
      </Middle>
    </Outer>)
};

const CompOne = (props) => {
  return (
    <div className="compOne">
      <p>One:</p>
      {props.children || props.content}
    </div>
  );
};

const CompTwo = (props) => {
  return (
    <div className="compTwo">
      <p>Two:</p>
      {props.children || props.content}
    </div>
  );
};

const CompThree = (props) => {
  return (
    <div className="compThree">
      <p>Three:</p>
      {props.children || props.content}
    </div>
  );
};

class App extends React.Component {
  constructor(props) {
    super(props);
  }
  
  render() {
  	const components = getComponentOrder();
      
    const SuperWrapped1 = 
    	superWrapped(components[0])(components[1])(components[2]);
    const SuperWrapped2 = 
    	superWrapped(components[2])(components[1])(components[0]);
    return (
    <div>
      <SuperWrapped1 content="Hello, world!" />
      <SuperWrapped2 content="Goodbye, world!" />
    </div>
    );
  }
}

const getComponentOrder = () => {
	return Math.random() < 0.5 ? 
    	[CompOne, CompTwo, CompThree] :
      Math.random() < 0.5 ?  
      	[CompThree, CompOne, CompTwo] :
        [CompTwo, CompOne, CompThree]
};

ReactDOM.render(<App />, document.getElementById('root'));
&#13;
.compOne {
  border: 5px solid red;
  padding: 5px;
  margin: 5px;
  display: inline-block;
}

.compTwo {
  border: 5px solid green;
  padding: 5px;
  margin: 5px;
  display: inline-block;
}

.compThree {
  border: 5px solid blue;
  padding: 5px;
  margin: 5px;
  display: inline-block;
}
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

<div id="root"></div>
&#13;
&#13;
&#13;