##一. 高階組件##
1.1 認(rèn)識(shí)高階組件###
高階函數(shù)####
維基百科定義,至少滿(mǎn)足以下條件之一:
- 接受一個(gè)或多個(gè)函數(shù)作為輸入坯门;
- 輸出一個(gè)函數(shù)。
JS中常見(jiàn)的filter,map把沼,reduce都是高階函數(shù)。
那什么是高階組件呢吁伺?
- 全名為Higher-Order Components饮睬,簡(jiǎn)稱(chēng)HOC。
- 官方定義篮奄,HOC是參數(shù)為組件捆愁,返回值為新組件的函數(shù)
也就是說(shuō):
- 高階函數(shù)割去,本身,不是組件牙瓢,是函數(shù)劫拗。
- 其參數(shù)是,組件矾克,返回值也是組件页慷。
const EnhancedComponent = higherOrderComponent(WrappedComponent);
注意:給高階組件傳遞屬性時(shí),需要在高階組件內(nèi)部進(jìn)行相應(yīng)接收胁附。
import React, { PureComponent } from 'react'
class App extends PureComponent {
render() {
return (
<div>
App:{this.props.name}
</div>
)
}
}
function enhanceComponent(WrappedComponent) {
return class extends PureComponent {
render() {
return <WrappedComponent {...this.props}/>
}
}
}
const EnhanceComponent = enhanceComponent(App);
// 導(dǎo)出的為自定義的高階組件
export default EnhanceComponent;
// index.js中的DOM渲染
ReactDOM.render(<App name="why"/>,document.getElementById('root'));
**補(bǔ)充:
- 高階組件并不是React API的一部分酒繁,它是基于React的組合特性而形成的設(shè)計(jì)模式;
- 在一些第三方庫(kù)中很常見(jiàn):如redux中的connect控妻;react-router中的withRouter州袒;
**
1.2 高階組件的使用###
1.2.1 props的增強(qiáng)####
- 不修改原有代碼的情況,添加新的props
``
import React, { PureComponent } from 'react'
// 定義一個(gè)高階組件
function enhanceRegionProps(WrappedComponent) {
return props => {
return <WrappedComponent {...props} region="中國(guó)" />
}
}
class Home extends PureComponent {
render() {
return <h2>Home:{昵稱(chēng):${this.props.nickname} 等級(jí):${this.props.level} 區(qū)域:${this.props.region}
}</h2>
}
}
class About extends PureComponent {
render() {
return <h2>About:{昵稱(chēng):${this.props.nickname} 等級(jí):${this.props.level} 區(qū)域:${this.props.region}
}</h2>
}
}
const EnhanceHome = enhanceRegionProps(Home);
const EnhanceAbout = enhanceRegionProps(About);
class App extends PureComponent {
render() {
return (
<div>
App:
<EnhanceHome nickname="coderwwq" level={90}/>
<EnhanceAbout nickname="kobe" level={99}/>
</div>
)
}
}
export default App;
2. 利用高階組件來(lái)共享Context
利用withUser弓候,**劫持props**
import React, { PureComponent, createContext } from 'react'
// 定義一個(gè)高階組件
function withUser(WrappedComponent) {
return props => {
return (
<UserContext.Consumer>
{
user => {
return <WrappedComponent {...props} {...user} />
}
}
</UserContext.Consumer>
)
}
}
// 創(chuàng)建Context
const UserContext = createContext({
nickname: "默認(rèn)",
level: -1,
region: "中國(guó)"
})
class Home extends PureComponent {
render() {
return <h2>Home:{昵稱(chēng):${this.props.nickname} 等級(jí):${this.props.level} 區(qū)域:${this.props.region}
}</h2>
}
}
class About extends PureComponent {
render() {
return <h2>About:{昵稱(chēng):${this.props.nickname} 等級(jí):${this.props.level} 區(qū)域:${this.props.region}
}</h2>
}
}
const UserHome = withUser(Home);
const UserAbout = withUser(About);
class App extends PureComponent {
render() {
return (
<div>
App:
<UserContext.Provider value={{ nickname: "wwq", level: 18, region: "中國(guó)" }}>
<UserHome />
<UserAbout />
</UserContext.Provider>
</div>
)
}
}
export default App;
####**1.2.2 渲染判斷鑒權(quán)**####
- 某些頁(yè)面必須用戶(hù)登陸成功才能進(jìn)入郎哭;
- 如果用戶(hù)沒(méi)有登陸成功,那么直接跳轉(zhuǎn)到登陸頁(yè)面菇存;
- **劫持jsx**
import React, { PureComponent } from 'react'
class LoginPage extends PureComponent{
render() {
return <h2>LoginPage</h2>
}
}
function withAuth(WrappedComponent) {
const NewCpn = props => {
const {isLogin} = props;
if (isLogin) {
return <WrappedComponent {...props}/>
} else {
return <LoginPage/>
}
}
NewCpn.displayName = "AuthCpn"
return NewCpn;
}
// 購(gòu)物車(chē)組件
class CartPage extends PureComponent {
render() {
return <h2>CartPage</h2>
}
}
const AuthCartPage = withAuth(CartPage);
export default class App extends PureComponent {
render() {
return (
<div>
<AuthCartPage isLogin={false}/>
</div>
)
}
}
####**1.2.3 生命周期劫持**####
import React, { PureComponent } from 'react'
class Home extends PureComponent {
// 即將渲染獲取一個(gè)時(shí)間
// 該API即將廢棄
componentWillMount(){
this.beginTime = Date.now();
}
render() {
return <h2>Home:</h2>
}
// 渲染完成再獲取一個(gè)時(shí)間
componentDidMount(){
this.endTime = Date.now();
const interval = this.endTime - this.beginTime;
console.log(Home渲染時(shí)間:${interval}
);
}
}
class About extends PureComponent {
render() {
return <h2>About:</h2>
}
}
export default class App extends PureComponent {
render() {
return (
<div>
<Home/>
<About/>
</div>
)
}
}
- 上面代碼進(jìn)行高階組件封裝
import React, { PureComponent } from 'react'
function withRenderTime(WrappedComponent) {
return class extends PureComponent {
componentWillMount(){
this.beginTime = Date.now();
}
componentDidMount(){
this.endTime = Date.now();
const interval = this.endTime - this.beginTime;
console.log(${WrappedComponent.name}渲染時(shí)間:${interval}
);
}
render() {
return <WrappedComponent {...this.props}/>
}
}
}
class Home extends PureComponent {
render() {
return <h2>Home:</h2>
}
}
class About extends PureComponent {
render() {
return <h2>About:</h2>
}
}
const TimeHome = withRenderTime(Home);
const TimeAbout = withRenderTime(About);
export default class App extends PureComponent {
render() {
return (
<div>
<TimeHome/>
<TimeAbout/>
</div>
)
}
}
**注意:不要?jiǎng)?chuàng)建自己的組件基類(lèi)夸研,代碼重用的主要方式是組合而不是繼承。**
###**1.3 高階函數(shù)的意義**###
- 針對(duì)某些代碼進(jìn)行優(yōu)雅的處理依鸥。
- 不修改原有代碼的情況下亥至,添加新的props
**缺陷**:
- HOC需要在原組件上包裹或者嵌套,如果大量適用HOC贱迟,將會(huì)產(chǎn)生非常多的嵌套姐扮,調(diào)試?yán)щy。
- HOC可以劫持props衣吠,在不遵守約定的情況下可能造成沖突茶敏。
**##二. 組件補(bǔ)充##**
- 函數(shù)組件不能用ref。
- ref不是屬性缚俏,交由React內(nèi)部管理惊搏。
- 獲取函數(shù)組件元素,用forwardRef高階組件袍榆。
###**2.1 ref轉(zhuǎn)發(fā)**###
- forwardRef給原函數(shù)組件進(jìn)行增強(qiáng)胀屿,加入一個(gè)ref屬性。
- 在hooks中會(huì)更簡(jiǎn)單包雀,后話(huà)宿崭。
import React, { PureComponent, createRef, forwardRef } from 'react'
class Home extends PureComponent {
render() {
return <h2>Home</h2>
}
}
// 已經(jīng)封裝好的高階組件
const Profile = forwardRef(function(props, ref){
console.log(props.name);
return <h2 ref={ref}>Profile</h2>
})
export default class App extends PureComponent {
constructor(props){
super(props);
this.titleRef = createRef();
this.homeRef = createRef();
this.profileRef = createRef();
}
render() {
return (
<div>
<h2 ref={this.titleRef}>Hello World</h2>
<Home ref={this.homeRef}/>
<Profile ref={this.profileRef}/>
<button onClick={e=>this.printRef()}>打印ref</button>
</div>
)
}
printRef(){
console.log(this.titleRef.current);
console.log(this.homeRef.current);
console.log(this.profileRef.current);
}
}
###**2.2 Portals**###
- 某些情況下,我們希望渲染的內(nèi)容獨(dú)立于父組件才写,甚至是獨(dú)立于當(dāng)前掛載到的DOM元素中(默認(rèn)都是掛載到id為root的DOM元素上的)葡兑。
- 希望渲染的組件完全獨(dú)立奖蔓,例如,彈窗讹堤。
- 彈窗直接放到body中吆鹤。
- css完全居中。
Protal提供了一種將子節(jié)點(diǎn)渲染到存在于父組件以外的DOM節(jié)點(diǎn)的優(yōu)秀方案:
- 第一個(gè)參數(shù)child是任何可渲染的React子1元素洲守;
- 第二個(gè)參數(shù)container是一個(gè)DOM元素疑务。
####Modal組件案例:將子組件渲染到屏幕的中間位置####
import React, { PureComponent } from 'react'
import ReactDOM from 'react-dom'
class Modal extends PureComponent {
render() {
return ReactDOM.createPortal(
this.props.children,
document.getElementById("modal")
)
}
}
class Home extends PureComponent {
render() {
return (
<div>
<h2>Home</h2>
<Modal>
<h2>Title</h2>
</Modal>
</div>
)
}
}
export default class App extends PureComponent {
render() {
return (
<div>
<Home/>
</div>
)
}
}
###**2.3 Fragment**###
###**2.4 StrictMode嚴(yán)格模式**###
檢測(cè):
- 不安全的生命周期
- 使用過(guò)時(shí)的ref API
- 使用廢棄的findDOMNode方法
- 以外的副作用
- 過(guò)時(shí)的context API
####