React.js:将一个组件包装到另一个组件中
许多模板语言都有“插槽”或“合格”语句,这些语句允许做一些控制反转来将一个模板包装到另一个模板中。
Angular有“transclude”选项 。
Rails有收益声明 。 如果React.js有yield语句,它将如下所示:
var Wrapper = React.createClass({ render: function() { return ( <div className="wrapper"> before <yield/> after </div> ); } }); var Main = React.createClass({ render: function() { return ( <Wrapper><h1>content</h1></Wrapper> ); } });
期望的输出:
<div class="wrapper"> before <h1>content</h1> after </div>
唉,React.js没有<yield/>
。 如何定义Wrapper组件来实现相同的输出?
尝试:
var Wrapper = React.createClass({ render: function() { return ( <div className="wrapper"> before {this.props.children} after </div> ); } });
请参阅文档中的多个组件:儿童和儿童 types以获取更多信息。
最简单的解决scheme:运行时包装
const Wrapper = ({children}) => ( <div> <div>header</div> <div>{children}</div> <div>footer</div> </div> ); const App = ({name}) => <div>Hello {name}</div>; const WrappedApp = () => ( <Wrapper> <App/> </Wrapper> );
这个解决scheme是最习惯的,但是Wrapper
每次都要收到一个新的children
道具。 不要担心,90%的用例当然是好的)。
更好的解决scheme:高阶组件(HOC)。
const wrapHOC = (WrappedComponent) => (props) => ( <div> <div>header</div> <div><WrappedComponent {...props}/></div> <div>footer</div> </div> ); const App = ({name}) => <div>Hello {name}</div>; const WrappedApp = wrapHOC(App);
为了获得更好的性能,使用高阶组件/ HOC (现在正式添加到React文档中 )比运行时包装更好,因为HOC通常可以使用shouldComponentUpdate
更快地将渲染shouldComponentUpdate
。
在这里我保持简单的例子,但显然如果你想利用这个性能增益,你应该使用一个PureComponent
而不是一个无状态的function组件(令人惊讶的是不会优化与shouldComponentUpdate
)
const wrapHOC = (WrappedComponent) => (props) => { class Wrapper extends React.PureComponent { render() { return ( <div> <div>header</div> <div><WrappedComponent {...this.props}/></div> <div>footer</div> </div> ); } } return Wrapper; }
现在,如果name
道具不改变,HOC将能够将渲染短路一步。
其他奇特的包装模式:
孩子可以是任何东西 :它可能会令人惊讶,但你可以随意使用任何你想要的作为children
道具。 一些相当普遍的做法是使用函数children 。
<State initial={0}> {(val, set) => ( <div onClick={() => set(val + 1)}> clicked {val} times </div> )} </State>
你可以变得更花哨,甚至提供一个对象
<Promise promise={getSomeAsyncData()}> {{ loading: () => <div>...</div>, success: (data) => <div>{data.something}</div>, error: (e) => <div>{e.message}</div>, }} </Promise >
HOC的论据可以是任何东西
以同样的方式,您可以自由地为您的HOC提供额外的参数。 请查看使用mapStateToProps
函数和选项的react-redux的 connect
示例。
您甚至可以将儿童function和HOC混合在一起:以下是Guillermo Rauch开源博客中的示例用法: HOC / 用法
export default withViews(({ views }) => ( <div> This blog post has been viewed {views} times ..... </div> )
结论
更高级的组件可以给你更好的性能。 这并不复杂,但起初看起来并不友好。
阅读完此代码后,不要将整个代码库迁移到HOC。 请记住,在应用程序的关键path中,出于性能方面的考虑,您可能希望使用HOC而不是运行时包装程序,特别是如果使用相同的包装程序很多次,则值得考虑将其作为HOC。
Redux首先使用一个运行时包装器<Connect>
然后为了性能的原因切换到HOC connect(options)(Comp)
。 这是我想在这个答案中突出显示的完美例证。
除了Ben的回答之外,我还发现了一个用于发送子元素types的用法,就是这样做的:
var ListView = React.createClass({ render: function() { var items = this.props.data.map(function(item) { return this.props.delegate({data:item}); }.bind(this)); return <ul>{items}</ul>; } }); var ItemDelegate = React.createClass({ render: function() { return <li>{this.props.data}</li> } }); var Wrapper = React.createClass({ render: function() { return <ListView delegate={ItemDelegate} data={someListOfData} /> } });