高阶组件在 react router 和 redux 等都会用到,本来想偷懒,直接跳过,真实怕啥来啥。
高阶组件本质上就是一个函数,传入的是一个组件,函数内部新创建了一个组件,这个新组件内部包裹了一层传进来的组件并对其进行处理,最后返回这个新组件。
高阶组件应用场景
1.增强props
场景一:通过高阶函数给传入的组件添加props参数
redux connect 函数中体现较明显
import React, { PureComponent } from 'react';
// 定义一个高阶组件
function enhanceRegionProps(WrappedComponent) {
return props => {
// {...props} 保留原来的参数
return <WrappedComponent {...props} region="中国"/>
}
}
class Home extends PureComponent {
render() {
return <h2>Home: {`昵称: ${this.props.nickname} 等级: ${this.props.level} 区域: ${this.props.region}`}</h2>
}
}
class About extends PureComponent {
render() {
return <h2>About: {`昵称: ${this.props.nickname} 等级: ${this.props.level} 区域: ${this.props.region}`}</h2>
}
}
// 通过模块导入时组件名可以不变
const EnhanceHome = enhanceRegionProps(Home);
const EnhanceAbout = enhanceRegionProps(About);
class App extends PureComponent {
render() {
return (
<div>
App
<EnhanceHome nickname="coderwhy" level={90}/>
<EnhanceAbout nickname="kobe" level={99}/>
</div>
)
}
}
export default App;
场景二:原组件用到了context,此时有些原来没用context的组件需要用到context中的一些参数,如果不使用高阶组件,有多少组件用到了context,就要修改多少个组价中的代码,这是比较麻烦的。但是通过高阶组件,则可以props形式向原组件传入context中的属性值。
withRouter 中体现较明显
原始做法:
import React, { PureComponent, createContext } from 'react';
// 创建Context
const UserContext = createContext({
nickname: "默认",
level: -1,
区域: "中国"
});
class Home extends PureComponent {
render() {
return (
<UserContext.Consumer>
{
user => {
return <h2>Home: {`昵称: ${user.nickname} 等级: ${user.level} 区域: ${user.region}`}</h2>
}
}
</UserContext.Consumer>
)
}
}
class About extends PureComponent {
render() {
return (
<UserContext.Consumer>
{
user => {
return <h2>About: {`昵称: ${user.nickname} 等级: ${user.level} 区域: ${user.region}`}</h2>
}
}
</UserContext.Consumer>
)
}
}
class App extends PureComponent {
render() {
return (
<div>
App
<UserContext.Provider value={{nickname: "why", level: 90, region: "中国"}}>
<Home/>
<About/>
</UserContext.Provider>
</div>
)
}
}
export default App;
通过高阶组件,可以大大缩减代码量:
import React, { PureComponent, createContext } from 'react';
// 定义一个高阶组件
function withUser(WrappedComponent) {
return props => {
return (
<UserContext.Consumer>
{
user => {
// 核心,将userContext的属性值以props形式一并传入进去
return <WrappedComponent {...props} {...user}/>
}
}
</UserContext.Consumer>
)
}
}
// 创建Context
const UserContext = createContext({
nickname: "默认",
level: -1,
区域: "中国"
});
class Home extends PureComponent {
render() {
return <h2>Home: {`昵称: ${this.props.nickname} 等级: ${this.props.level} 区域: ${this.props.region}`}</h2>
}
}
class About extends PureComponent {
render() {
return <h2>About: {`昵称: ${this.props.nickname} 等级: ${this.props.level} 区域: ${this.props.region}`}</h2>
}
}
class Detail extends PureComponent {
render() {
return (
<ul>
<li>{this.props.nickname}</li>
<li>{this.props.level}</li>
<li>{this.props.region}</li>
</ul>
)
}
}
const UserHome = withUser(Home);
const UserAbout = withUser(About);
const UserDetail = withUser(Detail);
class App extends PureComponent {
render() {
return (
<div>
App
<UserContext.Provider value={{nickname: "why", level: 90, region: "中国"}}>
<UserHome/>
<UserAbout/>
<UserDetail/>
</UserContext.Provider>
</div>
)
}
}
export default App;
2.登录鉴权
某些页面必须是登录后才能进入,如果未登录,则跳转到登录页面。
在不使用高阶组件的条件下,对每个需要鉴权的组件都需要做判断,通过高阶组件可以批量操作。
import React, { PureComponent } from 'react';
// 登录页面
class LoginPage extends PureComponent {
render() {
return <h2>LoginPage</h2>
}
}
function withAuth(WrappedComponent) {
// 传进来的组件需要有isLogin属性
const NewCpn = props => {
const {isLogin} = props;
if (isLogin) {
return <WrappedComponent {...props}/>
} else {
return <LoginPage/>
}
}
// .displayName
NewCpn.displayName = "AuthCpn"
return NewCpn;
}
// 购物车组件
class CartPage extends PureComponent {
render() {
return <h2>CartPage</h2>
}
}
const AuthCartPage = withAuth(CartPage);
export default class App extends PureComponent {
render() {
return (
<div>
<AuthCartPage isLogin={true}/>
</div>
)
}
}
3.生命周期劫持
前面的两种情况,第一种可以看做是劫持了props,第二种可看做是劫持了 jsx,还有一种就是劫持生命周期。
场景:利用生命周期函数,计算组件渲染所花费的时间。
原始版:缺点是每个需要计算时间的组件都需要重复写周期函数的代码。
import React, { PureComponent } from 'react';
class Home extends PureComponent {
// 即将渲染获取一个时间 beginTime
UNSAFE_componentWillMount() {
this.beginTime = Date.now();
}
// 渲染完成再获取一个时间 endTime
componentDidMount() {
this.endTime = Date.now();
const interval = this.endTime - this.beginTime;
console.log(`Home渲染时间: ${interval}`)
}
render() {
return <h2>Home</h2>
}
}
class About extends PureComponent {
// 即将渲染获取一个时间 beginTime
UNSAFE_componentWillMount() {
this.beginTime = Date.now();
}
// 渲染完成再获取一个时间 endTime
componentDidMount() {
this.endTime = Date.now();
const interval = this.endTime - this.beginTime;
console.log(`About渲染时间: ${interval}`)
}
render() {
return <h2>About</h2>
}
}
export default class App extends PureComponent {
render() {
return (
<div>
<Home />
<About />
</div>
)
}
}
使用高阶组件劫持生命周期:
import React, { PureComponent } from 'react';
function withRenderTime(WrappedComponent) {
return class extends PureComponent {
// 即将渲染获取一个时间 beginTime
UNSAFE_componentWillMount() {
this.beginTime = Date.now();
}
// 渲染完成再获取一个时间 endTime
componentDidMount() {
this.endTime = Date.now();
const interval = this.endTime - this.beginTime;
// WrappedComponent.name 组件名称(及类名称,每个类都会有一个name属性)
console.log(`${WrappedComponent.name}渲染时间: ${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>
)
}
}
class Person {
}
console.log(Person.name);
组件化补充
1.ref 的转发
典型代表是 forwardRef。众所周知,函数组件是不能添加ref属性的(严格来说ref并不是属性,即不会以props形式传递,而是单独由react控制的)。但如果想给函数组件中的某个标签添加ref,则可用forwardRef,其本质上就是一个高阶组件。
import React, { PureComponent, createRef, forwardRef } from 'react';
class Home extends PureComponent {
render() {
return <h2>Home</h2>
}
}
// 高阶组件forwardRef
const Profile = forwardRef(function(props, ref) {
return <p ref={ref}>Profile</p>
})
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} name={"why"}/>
<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.portals
场景:点击某一子组件中的按钮,在屏幕中间弹出弹窗。
import React, { PureComponent } from 'react';
import ReactDOM from 'react-dom';
class Modal extends PureComponent {
render() {
return ReactDOM.createPortal(
// this.props.children 表示所有的子组件
this.props.children,
// 将其渲染到id为modal的标签中
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>
)
}
}
3.fragment
某些情况下,不想用div嵌套多个标签,可以用fragment
import React, { PureComponent, Fragment } from 'react';
export default class App extends PureComponent {
constructor(props) {
super(props);
this.state = {
counter: 0,
friends: [
{name: "why", age: 18},
{name: "lilei", age: 20},
{name: "kobe", age: 25},
]
}
}
render() {
return (
<>
<h2>当前计数: {this.state.counter}</h2>
<button onClick={e => this.increment()}>+1</button>
<div>
{
this.state.friends.map((item, index) => {
return (
<Fragment key={item.name}>
<div>{item.name}</div>
<p>{item.age}</p>
<hr/>
</Fragment>
)
})
}
</div>
</>
)
}
increment() {
this.setState({
counter: this.state.counter + 1
})
}
}

本文详细介绍了React中的高阶组件(Higher-Order Components, HOC)的概念和应用场景,包括增强props、登录鉴权、生命周期劫持等功能,并通过具体示例展示了如何使用HOC简化代码,提高代码复用性和组件的灵活性。此外,还提到了ref的转发、Portals以及Fragment在React开发中的应用。
1464

被折叠的 条评论
为什么被折叠?



