引入及概念
1.js中高阶函数:一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数。
function add(x, y, f) {
return f(x) + f(y);
}
//当调用add(-5, 6, Math.abs)时,参数x,y和f分别接收-5,6和函数Math.abs,根据函数定义,可以推导计算过程为:
//x = -5;
//y = 6;
//f = Math.abs;
//f(x) + f(y) ==> Math.abs(-5) + Math.abs(6) ==> 11;
//return 11;
//用代码验证一下:
add(-5, 6, Math.abs); // 11
2.类似于高阶函数,高阶组件(Higher-Order Components)概念如下:高阶组件就是一个函数,且该函数接受一个组件作为参数,并返回一个新的组件
const EnhancedComponent = higherOrderComponent(WrappedComponent);
3.应用场景
(1)react-redux的connect函数~
//把redux的state和action传进去用来创建高阶函数,
//通过props注入给了Component。
//被包装后的Component可以直接用this.props去调用redux state和action创建函数了。
ConnectedComment = connect(mapStateToProps, mapDispatchToProps)(Component);
即分解如下:
// connect是一个返回函数的函数(就是个高阶函数)
const enhance = connect(mapStateToProps, mapDispatchToProps);
// 返回的函数就是一个高阶组件,该高阶组件返回一个与Redux store
// 关联起来的新组件
const ConnectedComment = enhance(Component);
(2)antd中form表单包装
const WrappedNormalLoginForm = Form.create()(NormalLoginForm);
4.高阶组件的意义
高阶组件就是一个没有副作用的纯函数。
最普通的组件哦。
welcome函数转为react组件。
import React, {Component} from 'react'
class Welcome extends Component {
constructor(props) {
super(props);
this.state = {
username: ''
}
}
componentWillMount() {
let username = localStorage.getItem('username');
this.setState({
username: username
})
}
render() {
return (
<div>welcome {this.state.username}</div>
)
}
}
export default Welcome;
goodbey函数转为react组件。
import React, {Component} from 'react'
class Goodbye extends Component {
constructor(props) {
super(props);
this.state = {
username: ''
}
}
componentWillMount() {
let username = localStorage.getItem('username');
this.setState({
username: username
})
}
render() {
return (
<div>goodbye {this.state.username}</div>
)
}
}
export default Goodbye;
问题:上面两段代码相同,都市从localstorange中拿到username,解决:写一个通用的高阶组件。
import React, {Component} from 'react'
export default (WrappedComponent) => {
class NewComponent extends Component {
constructor() {
super();
this.state = {
username: ''
}
}
componentWillMount() {
let username = localStorage.getItem('username');
this.setState({
username: username
})
}
render() {
return <WrappedComponent username={this.state.username}/>
}
}
return NewComponent
}
使用高阶函数封装组件:
import React, {Component} from 'react';
import wrapWithUsername from 'wrapWithUsername';
class Welcome extends Component {
render() {
return (
<div>welcome {this.props.username}</div>
)
}
}
Welcome = wrapWithUsername(Welcome);
export default Welcome;
import React, {Component} from 'react';
import wrapWithUsername from 'wrapWithUsername';
class Goodbye extends Component {
render() {
return (
<div>goodbye {this.props.username}</div>
)
}
}
Goodbye = wrapWithUsername(Goodbye);
export default Goodbye;
高阶组件就是把username通过props传递给原始组件了。原始组件只管从props里面拿来用就好了。
使用注意
1.不要在render里使用高阶组件。
原因:性能问题+重新加载一个组件会引起原有组件的所有状态和子组件丢失
解决:组件外使用(推荐)、构造函数里、生命周期里使用
render() {
// 每一次render函数调用都会创建一个新的EnhancedComponent实例
// EnhancedComponent1 !== EnhancedComponent2
const EnhancedComponent = enhance(MyComponent);
// 每一次都会使子对象树完全被卸载或移除
return <EnhancedComponent />;
}
2.必须将静态方法做拷贝
当使用高阶组件包装组件,原始组件被容器组件包裹,也就意味着新组件会丢失原始组件的所有静态方法。
解决:
(1)将原始组件的所有静态方法全部拷贝给新组件:
function enhance(WrappedComponent) {
class Enhance extends React.Component {/*...*/}
// 必须得知道要拷贝的方法 :(
Enhance.staticMethod = WrappedComponent.staticMethod;
return Enhance;
}
点评:你需要清楚的知道都有哪些静态方法需要拷贝
(2)使用hoist-non-react-statics自动拷贝所有非React的静态方法:
import hoistNonReactStatic from 'hoist-non-react-statics';
function enhance(WrappedComponent) {
class Enhance extends React.Component {/*...*/}
hoistNonReactStatic(Enhance, WrappedComponent);
return Enhance;
}
(3)分别导出组件静态方法
// 替代……
MyComponent.someFunction = someFunction;
export default MyComponent;
// ……分别导出……
export { someFunction };
// ……在要使用的组件中导入
import MyComponent, { someFunction } from './MyComponent.js';
3.不能传递refs属性
refs是一个伪属性,React对它进行了特殊处理。如果你向一个由高阶组件创建的组件的元素添加ref应用,那么ref指向的是最外层容器组件实例的,而不是内层包裹组件。
使用例子后续补充。。。^_^
参考链接:
http://react-china.org/t/react-higher-order-components/14949
https://doc.react-china.org/docs/higher-order-components.html
欢迎指正!