其实render props就是能动态决定组件要渲染什么内容,话很少说,咱们仍是照样贴代码,react
在具体场景和需求中去理解es6
// <Mouse> 组件封装了咱们须要的行为...
class Mouse extends React.Component {
constructor(props) {
super(props);
this.handleMouseMove = this.handleMouseMove.bind(this);
this.state = { x: 0, y: 0 };
}
handleMouseMove(event) {
this.setState({
x: event.clientX,
y: event.clientY
});
}
render() {
return (
<div style={{ height: '100%' }} onMouseMove={this.handleMouseMove}>
{/* ...但咱们如何渲染 <p> 之外的东西? */}
<p>The current mouse position is ({this.state.x}, {this.state.y})</p>
</div>
);
}
}
class MouseTracker extends React.Component {
render() {
return (
<div>
<h1>移动鼠标!</h1>
<Mouse />
</div>
);
}
}复制代码
在这里咱们有一个mouse组件,能够监测到鼠标的位置,而后咱们引用了这个组件,在外面加个一个h1标题,但咱们仍是没能去改变mouse组件里面的内容,如何渲染mouse组件出p以外的内容,咱们继续往下看,举个例子,假设咱们有一个 <Cat>
组件,它能够呈现一张在屏幕上追逐鼠标的猫的图片。咱们或许会使用 <Cat mouse={{ x, y }}
prop 来告诉组件鼠标的坐标以让它知道图片应该在屏幕哪一个位置。若是按照咱们的正常写法应该是这样redux
class Cat extends React.Component {
render() {
const mouse = this.props.mouse;
return (
<img src="/cat.jpg" style={{ position: 'absolute', left: mouse.x, top: mouse.y }} />
);
}
}
class MouseWithCat extends React.Component {
constructor(props) {
super(props);
this.handleMouseMove = this.handleMouseMove.bind(this);
this.state = { x: 0, y: 0 };
}
handleMouseMove(event) {
this.setState({
x: event.clientX,
y: event.clientY
});
}
render() {
return (
<div style={{ height: '100%' }} onMouseMove={this.handleMouseMove}>
{/*
咱们能够在这里换掉 <p> 的 <Cat> ......
可是接着咱们须要建立一个单独的 <MouseWithSomethingElse>
每次咱们须要使用它时,<MouseWithCat> 是否是真的能够重复使用.
*/}
<Cat mouse={this.state} />
</div>
);
}
}
class MouseTracker extends React.Component {
render() {
return (
<div>
<h1>移动鼠标!</h1>
<MouseWithCat />
</div>
);
}
}复制代码
这里咱们至关因而重写了组件,那若是咱们使用render propsbash
class Cat extends React.Component {
render() {
const mouse = this.props.mouse;
return (
<img src={CatJpg} style={{position: 'absolute', left: mouse.x, top: mouse.y}}/>
);
}
}
class Mouse extends React.Component {
constructor(props) {
super(props);
this.handleMouseMove = this.handleMouseMove.bind(this);
this.state = {x: 0, y: 0};
}
handleMouseMove(event) {
this.setState({
x: event.clientX,
y: event.clientY
});
}
render() {
return (
<div style={{height: '100%'}} onMouseMove={this.handleMouseMove}>
{/*
Instead of providing a static representation of what <Mouse> renders,
use the `render` prop to dynamically determine what to render.
*/}
{this.props.render(this.state)} //这里的render其实就是Mouse的prop
</div>
);
}
}
export default class MouseTracker extends React.Component {
render() {
return (
<div>
<h1>移动鼠标!</h1>
<Mouse render={mouse => (
<Cat mouse={mouse}/> //mouse参数就是从上面传来的
)}/>
</div>
);
}
}复制代码
这里使用render props咱们完美实现了,但咱们是否可使用别的方法呢,这里咱们用children实现app
class Cat extends React.Component {
render() {
const mouse = this.props.mouse;
return (
<img src={CatJpg} style={{position: 'absolute', left: mouse.x, top: mouse.y}}/>
);
}
}
class Mouse extends React.Component {
constructor(props) {
super(props);
this.handleMouseMove = this.handleMouseMove.bind(this);
this.state = {x: 0, y: 0};
}
handleMouseMove(event) {
this.setState({
x: event.clientX,
y: event.clientY
});
}
render() {
return (
<div style={{height: '100%'}} onMouseMove={this.handleMouseMove}>
{/*
Instead of providing a static representation of what <Mouse> renders,
use the `render` prop to dynamically determine what to render.
*/}
{this.props.children(this.state)}
</div>
);
}
}
export default class MouseTracker extends React.Component {
render() {
return (
<div>
<h1>移动鼠标!</h1>
<Mouse>
{(mouse) => <Cat mouse={mouse}/>}
</Mouse>
</div>
);
}
}复制代码
既然讲到children,也能够看看这个函数
看到这里就有人说了,那你用render Props其实也是改变了源组件,但其实并不相同,这里能够理解成咱们把Mouse组件变成了一个更具备扩展性的组件,就像是提供了一个接口工具
若是说render Props能动态决定组件要渲染什么内容,那高阶函数(下面都简称为HOC吧)就是在不改变源组件的前提下,对源组件进行加强,而后返回一个新的高阶组件post
import React, {Component} from 'react';
function withHeader(title) {
return function (WrappedComponent) {
return class HOC extends Component {
render() {
return <div>
<div className="demo-header">
{title
? title
: '我是标题'}
</div>
<WrappedComponent {...this.props}/>
</div>
}
}
}
}
//@withHeader //在这里可使用es7的decorator,es7的decorator能够在最后的相关连接观看
class Demo extends Component {
render() {
return (
<div>
我是一个普通组件
</div>
);
}
}
export default withHeader('title')(Demo);复制代码
Demo就是源组件,withHeader就是HOC,withHeader返回一个高阶组件优化
withHeader的第一个参数是用来决定组件中的title,而第二个参数则是WrappedComponent,便是被包裹组件ui
import React, {Component} from 'react';
function withHeader(title) {
return function (WrappedComponent) {
return class HOC extends Component {
render() {
const newProps = {
test:'hoc'
}
//即是将HOC中定义的props传到wrapperComponent中
return <div>
<div className="demo-header">
{title
? title
: '我是标题'}
</div>
<WrappedComponent {...this.props} {...newProps}/>
</div>
}
}
}
}
//@withHeader //在这里可使用es7的decorator,es7的decoratro咱们会在后面详解
class Demo extends Component {
render() {
return (
<div>
我是一个普通组件
</div>
);
}
}
export default withHeader('title')(Demo);
复制代码
反向继承应该是一个继承的做用,高阶函数能够去继承被包裹组件
mport React, {Component} from 'react';
function withHeader(WrappedComponent) {
return class HOC extends WrappedComponent {
//其实这里就是继承了wrapperComponent(被包裹组件)
componentDidMount(){
console.log(this.state);
// {second: 0} 这里打印出了被包裹组件的state
}
render() {
return super.render()
//这个代码我也不是很懂,就是es6的class中的语法
}
}
}
class Demo extends Component {
constructor(props) {
super(props);
this.state = { seconds: 0 };
}
render() {
return (
<div>
我是一个普通组件
</div>
);
}
}
export default withHeader(Demo);复制代码
好比你要对一个组件进行多个加强或者削弱,这时候你可能须要用到多个
import React, {Component} from 'react';
import _ from 'lodash';
function withHeader(WrappedComponent) {
return class HOC extends Component {
render() {
return <div>
<div className="demo-header">
我是标题
</div>
<WrappedComponent {...this.props}/>
</div>
}
}
}
function withHandSome(WrappedComponent) {
return class HOC extends Component {
render() {
return <div>
<div className="demo-header">
我说帅哥
</div>
<WrappedComponent {...this.props}/>
</div>
}
}
}
@withHeader
@withHandSome
//这里用es7 decorator一样能实现,可是这样写太复杂了,咱们可使用compose函数,许多第三方库都提供了
compose 工具函数,包括 lodash (好比 lodash.flowRight), Redux和 Ramda
class Demo extends Component {
render() {
return (
<div>
我是一个普通组件
</div>
);
}
}
export default withHandSome(withHeader(Demo)); //其实就是方法一层套一层复制代码
下面咱们试着将组合一下
import React, {Component} from 'react';
import _ from 'lodash';
function withHeader(WrappedComponent) {
return class HOC extends Component {
render() {
return <div>
<div className="demo-header">
我是标题
</div>
<WrappedComponent {...this.props}/>
</div>
}
}
}
function withHandSome(WrappedComponent) {
return class HOC extends Component {
render() {
return <div>
<div className="demo-header">
我说帅哥
</div>
<WrappedComponent {...this.props}/>
</div>
}
}
}
const enhance = _.flowRight([withHeader, withHandSome])
//这里我用了lodash的compose函数,compose函数的本质其实就是
compose(f, g, h) 等同于 (...args) => f(g(h(...args)))
@enhance
export default class Demo extends Component {
render() {
return (
<div>
我是一个普通组件
</div>
);
}
}
这里还要提到redux的connect,connect也是一个返回高阶组件的高阶函数
const ConnectedComment = connect(commentSelector, commentActions)(CommentList);
//等同于下面这种写法
// connect 是一个函数,它的返回值为另一个函数。
const enhance = connect(commentListSelector, commentListActions);
// 返回值为 HOC,它会返回已经链接 Redux store 的组件
const ConnectedComment = enhance(CommentList);
复制代码
HOC是es7Decorator
模式在React
的一种实现,它能够抽离公共逻辑,像洋葱同样层层叠加给组件,每一层职能分明,能够方便地抽离与增添。在优化代码或解耦组件时,能够考虑使用高阶组件模式。