其實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
的一種實現,它能夠抽離公共邏輯,像洋蔥同樣層層疊加給組件,每一層職能分明,能夠方便地抽離與增添。在優化代碼或解耦組件時,能夠考慮使用高階組件模式。