react高階元件淺談
阿新 • • 發佈:2019-01-28
引入及概念
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指向的是最外層容器元件例項的,而不是內層包裹元件。
使用例子後續補充。。。^_^
參考連結:
歡迎指正!