什么是 Render Props?
新的context api使用了
render props
:
<ThemeContext.Consumer>
{theme => (
<button
{...props}
style={{backgroundColor: theme.background}}
/>
)}
</ThemeContext.Consumer>
第一次见到这个语法时,大多会很惊讶,因为日常代码里
props.children
必然是字符串或者元素。但事实上
props.children
可以是函数,只要最终生成的render的返回值是dom元素就行。例如:
// chilren props
const Test = props => props.children('hello world')
const App = () => (
<Test>
{text => <div>{text}</div>}
</Test>
)
ReactDOM.render((<App />, root) // 返回<div>hello world</div>
虽然没有实际意义,但这即是一个
render props
。当然
render props
最初的意思是:
组件不自己定义render函数,而是通过一个名为
render
的
props
将外部定义的render函数传入使用。
以上例来说,会是这样:
// render props
const Test = props => props.render('hello world')
const App = () => (
<Test
render={text => <div>{text}</div>}
/>
)
ReactDOM.render((<App />, root) // 返回<div>hello world</div>
因为现实中render函数很庞大,为了代码整洁多半会使用
children
而不是自定义的
render
来接收外部的render函数。所以这一技巧也可以称为
children props
(相对于
render props
更加不知所云的名称),但一般统称
render props
。
为何要使用如此怪异的语法呢?
为了重用性。React的组件化就是为了方便重用。大多数场景我们需要重用的是UI(例如文章列表,侧栏),但也有少数情况需要重用的是功能和状态(例如context)。
如果说React的核心是
State => UI
, 普通的组件是UI重用,那么
render props
就是为了State重用而应运而生的。
Render Props 小史
在 Demo 展开前插播一段
render props
的趣史。
- 最早引人关注是从 Facebook 的 Cheng Lou 写的 React Motion 动画库。
import { Motion, spring } from 'react-motion';
<Motion defaultStyle={{x: 0}} style={{x: spring(10)}}>
{value => <div>{value.x}</div>}
</Motion>
-
之后这一写法被各位大牛广泛接受,写过很多非常赞的前端教程的 Kent C. Dodds 就非常喜欢 render props, 他所任职的 PayPal 的输入框组件 downshift 也使用了 render props
-
大家熟知的 react-router 的作者 Michael Jackson 也是 render props 的极力推崇者。他twitter过一句很有争议的话:
Next time you think you need a HOC (higher-order component) in @reactjs, you probably don't.
翻译过来就是:下次你想使用HOC解决问题时,其实大半不需要。 在回复中他补充说明到,
I can do anything you're doing with your HOC using a regular component with a render prop. Come fight me.
即是说,
所有用 HOC 完成的事,render props 都能搞定。
值得一提的是
react-router 4
里唯一的一个 HOC 是
withRouter
, 而它是用 render props 实现的,有兴趣的可以去看一下
源代码
。
-
HOC 虽然好用,但写一个“真正好用”的HOC却要经过一道道繁琐的工序(React的新api
fowardRef
就几乎是为此而生的),是个用着舒服写着烦的存在。所以感觉最近大有“少写 HOC 推崇 render props”的思潮。至于新的 Context api 虽然思路上和react-redux
如出一辙,却选择了 render props 的写法,在我看来也是顺理成章。
Render Props 使用场景
实例1: 一个日常的使用场景是弹窗。App的弹窗UI可能千奇百怪,但它们的功能却是类似的:无非有个显示隐藏的状态,和一个控制显隐的方法,以 antd 为例: