目前看来渲染属性并没有得到足够的关注,然而它被许多知名的React库广泛使用,例如react-router 4
,react motion
等。React网站也专门为其提供了一个专用的部分。这种模式的出现有什么原因?与通常所知的高阶组件(HOC)模式相比如何?
请为我的研究留下答案,不同的答案和讨论都非常欢迎!
HOC借鉴了高阶函数的概念:
高阶函数(也称为函数式、函数形式或函数子)是至少执行以下操作之一的函数:
- 将一个或多个函数作为参数(即过程参数),
- 将一个函数作为其结果返回。[有争议-讨论]
高阶组件(HOC)是React中用于重用组件逻辑的高级技术。
源自这个Gist。
该模式涉及到静态组合。核心/可重用的逻辑被封装在HOC中,同时将移动部分留给组件。
以react router中的withRouter为例:
每当渲染时,withRouter将更新的match、location和history props传递给包装的组件。
// 这避免了shouldComponentUpdate
withRouter(connect(...)(MyComponent))
现在,您会得到一个增强版的MyComponent,其中包含由路由器HOC传递的props:{ history,match,location,...connectProps,...ownProps }
。
常见的方法是
compose(
connect( ... ),
enhance( ... ),
withRouter( ... ),
lifecycle( ... ),
somethingElse( ... )
)(MyComponent);
很棒的部分是,你可以使用compose工具无限组合这些高阶组件,以获得最终增强版的组件,你的组件将从HOC返回的新组件中注入redux store、react router等知识。
这种方法的缺点是:
The behavior of the component is defined before runtime thus lost the power of react's rendering lifecycles, say you can't do something like:
compose(
this.state.shouldConnect && connect( ... ),
this.state.shouldEnhance && enhance( ... ),
this.state.shouldWithRouter && withRouter( ... ),
...
)(MyComponent);
since state
/props
is not available before your code runs.
Indirection & Naming collisions.
渲染属性是组件用来知道如何渲染的函数属性。
最初由react-motion采用,早在Dan's Gist提交redux的几周前就已经出现了。
这种模式涉及到动态组合。核心/可重用逻辑保留在组件中,而移动部分作为回调属性传递。
您可以通过渲染属性创建高阶组件。
仍然以withRouter为例:
const withRouter = Component => {
const C = props => {
const { wrappedComponentRef, ...remainingProps } = props;
return (
<Route
render={routeComponentProps => (
<Component
{...remainingProps}
{...routeComponentProps}
ref={wrappedComponentRef}
/>
)}
/>
);
};
...
return hoistStatics(C, Component);
};
而反之则不成立。
<Connect render={connectPropsMergedWithState => {
<Enhance render={enhancePropsMergedWithState => {
<WithRouter render={routerPropsMergedWithState => {
<Lifecycle render={lifecyclePropsMergedWithState => {
<SomethingElse render={somethingElsePropsMergedWithState => {
...
}/>
...
}/>
...
}/>
...
}/>
...
}/>
虽然它看起来不太好,但有很多好处。
众所周知的缺点是性能优化很棘手,因为接收哪些属性被推迟到运行时。也许不要做过早的优化可能不是一个好主意,但那可能完全是另一个话题。
如果您同意从react router 3转移到4的方向移动,渲染属性可能是您的选择。