写过react项目的应该都碰到过,不一样组件复用相同代码的问题,在react早期使用React.createClass建立组件的时代,咱们常用的是mixins来实现代码复用。好比有个组件A,它用来实时的获取鼠标的位置。react
//A组件 import React from 'react' import ReactDOM from 'react-dom' const App = React.createClass({ getInitialState() { return { x: 0, y: 0 } }, handleMouseMove(event) { this.setState({ x: event.clientX, y: event.clientY }) }, render() { const { x, y } = this.state return ( <div style={{ height: '100%' }} onMouseMove={this.handleMouseMove}> <h1>The mouse position is ({x}, {y})</h1> </div> ) } }) ReactDOM.render(<App/>, document.getElementById('app'))
若是此时有个组件B也想集成这个功能,咱们能够经过mixins,代码是这样的git
//B组件 import React from 'react' import ReactDOM from 'react-dom' const MouseMixin = { getInitialState() { return { x: 0, y: 0 } }, handleMouseMove(event) { this.setState({ x: event.clientX, y: event.clientY }) } } const App = React.createClass({ // Use the mixin! mixins: [ MouseMixin ], render() { const { x, y } = this.state return ( <div style={{ height: '100%' }} onMouseMove={this.handleMouseMove}> <h1>The mouse position is ({x}, {y})</h1> </div> ) } }) ReactDOM.render(<App/>, document.getElementById('app'))
很容易是吧~但委屈的是react16以后就再也不支持mixins了,由于es6普及了呀!es6
那怎么办呢?办法老是有的,HOC(高阶组件)的概念被提出来,什么是高阶组件?说白了其实就是把函数当作参数传入到另外一个函数中,在咱们展开高阶组件前,咱们先来想一想除了由于es6的普及react再也不支持mixins以外,mixins还有啥其它缺点不。固然是有的,有如下几点:github
因此为了代替mixins,不少人就提出了HOC(高阶组件),代码是下面这样的。react-router
import React from 'react' import ReactDOM from 'react-dom' const withMouse = (Component) => { return class extends React.Component { state = { x: 0, y: 0 } handleMouseMove = (event) => { this.setState({ x: event.clientX, y: event.clientY }) } render() { return ( <div style={{ height: '100%' }} onMouseMove={this.handleMouseMove}> <Component {...this.props} mouse={this.state}/> </div> ) } } } class App extends React.Component{ render() { // 代替直接处理state,咱们从props里得到x,y坐标 const { x, y } = this.props.mouse return ( <div style={{ height: '100%' }}> <h1>The mouse position is ({x}, {y})</h1> </div> ) } } //把App组件当作参数传到withMouse方法里面,在withMouse内部经过props得到x、y坐标值 const AppWithMouse = withMouse(App) ReactDOM.render(<AppWithMouse/>, document.getElementById('app'))
看起来很不错的样子!app
可是,回到以前mixins存在的问题,咱们想想,HOC有上述的问题么?咱们来看下:<br/>dom
个人天.....函数
幸运女神降临!
mmp,前面都是炮灰,到了划重点的时候啦!<br/>学习
Render Prop是个值为函数的属性,经过Render Prop,组件知道什么应该被渲染
很糊涂是否是,看代码ui
import React from 'react' import ReactDOM from 'react-dom' import PropTypes from 'prop-types' class Mouse extends React.Component { static propTypes = { render: PropTypes.func.isRequired } state = { x: 0, y: 0 } handleMouseMove = (event) => { this.setState({ x: event.clientX, y: event.clientY }) } render() { return ( <div style={{ height: '100%' }} onMouseMove={this.handleMouseMove}> {this.props.render(this.state)} </div> ) } } const App = React.createClass({ render() { return ( <div style={{ height: '100%' }}> <Mouse render={({ x, y }) => ( <h1>The mouse position is ({x}, {y})</h1> )}/> </div> ) } }) ReactDOM.render(<App/>, document.getElementById('app'))
看明白了么,这里咱们经过定义一个render属性,值是个函数,描述了咱们想要渲染的元素,而后在子组件里面调用该render方法,再回头看下以前的两个问题,难以溯源,如今主动权在父组件上,我要什么数据大家给我拿来就好了,大家子组件各自去实现,我只要结果不要过程,于是就不存在数据来源问题,命名空间的问题也没了。好厉害~~~。
最后偷偷的告诉大家一个更厉害的,上面的render方法里面咱们是直接写出了渲染x,y值,只适用于当前App组件,咱们能够经过高阶组件来达到为任何组件添加该功能,代码是这样的。
const withMouse = (Component) => { return class extends React.Component{ render() { return <Mouse render={mouse=>( <Component {...this.props} mouse={mouse}/> )}/> } } }
听说react-router源码里面为每一个组件增长路由属性就是经过该方法!
好了!大功完成了,欢迎一块儿讨论学习~
我的博客地址:意卿