高阶组件:函数传入一个组件,返回一个新的组件。
1、高阶组件的运用:
在不破坏原本的结构的时候,利用高阶组件进行加工。
如下,如果要在Home组件以及About组件再传入region=“中国”,就需要两个都修改。
class App extends PureComponent {
render() {
return (
<div>
App
<Home nickname="coderwhy" level={90}/>
<About nickname="kobe" level={99}/>
</div>
)
}
}
export default App;
使用高阶组件进行加工操作:
// 定义一个高阶组件
function enhanceRegionProps(WrappedComponent) {
return props => {
return <WrappedComponent {...props} region="中国"/>
}
}
class App extends PureComponent {
render() {
return (
<div>
App
<EnhanceHome nickname="coderwhy" level={90}/>
<EnhanceAbout nickname="kobe" level={99}/>
</div>
)
}
}
export default App;
2、增强props
可以利用高阶组件简化,例如下未简化共享数据context操作如下:
在其中,Home组件以及About组件有重复性代码。
// 创建Context
const UserContext = createContext({
nickname: "默认",
level: -1,
region: "中国"
});
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;
创捷高阶组件,进行简化:
// 定义一个高阶组件
function withUser(WrappedComponent) {
return props => {
return (
<UserContext.Consumer>
{
user => {
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;
3、高阶组件实现简单的登录鉴权操作
使用高阶组件进行判断是否是登陆状态再确定返回显示的组件。
其中的 NewCpn.displayName = "AuthCpn"是修改组件名。
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;
}
// 购物车组件
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>
)
}
}
4、高阶组件生命周期劫持
实现渲染时间的计算。
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;
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);
5、高阶组件之ref的转发
ref不能运用于函数式组件,函数式组件没有实例,所以不能获取到对应的组件对象。
如果想要获取函数式组件之中的某个DOM元素,可以使用高阶组件forwardRef.
下面是按照props属性的方法思维,但是是错误的,ref是由react底层进行管理的。props没有ref.
function Profile(props) {
return <p ref={props.ref}>Profile</p>
})
export default class App extends PureComponent {
constructor(props) {
super(props);
this.profileRef = createRef();
}
render() {
return (
<div>
<Profile ref={this.profileRef} name={"why"}/>
<button onClick={e => this.printRef()}>打印ref</button>
</div>
)
}
printRef() {
console.log(this.profileRef.current);
}
}
使用高阶组件forwardRef,组件代码修改为:
forwardRef让函数组件有第二个参数,ref
// 高阶组件forwardRef
const Profile = forwardRef(function(props, ref) {
return <p ref={ref}>Profile</p>
})
6、Portals的使用
某些情况下,渲染内容独立于父组件,甚至独立于当前挂载到的DOM元素中。就例如实现一个位于页面中间的组件,独立于其父组件,虽然代码位置写在父组件内但是挂载在DOM元素之中。
使用ReactDOM.createPortal(),第一个参数是子元素,第二个参数是挂载的DOM元素。代码如下:
其中的 组件就是要放于页面中间的组件。this.props.children是第一个参数,代表的是组件之中包含的内容。
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>
)
}
}