This article is from the #React tutorial series: https://mp.weixin.qq.com/mp/appmsgalbum?__biz=Mzg5MDAzNzkwNA==&action=getalbum&album_id=1566025152667107329 )
1. High-order components
1.1. Understanding higher-order components
What are higher-order components? I believe many students have heard of and used higher-order functions. They are very similar, so we can first review what higher-order functions are.
Wikipedia definition of higher-order function : at least one of the following conditions is met:
- Accepts one or more functions as input;
- Output a function;
The more common ones in JavaScript filter、map、reduce
are higher-order functions.
So what are higher-order components?
- The English name for high-order components is Higher-Order Components , or HOC for short ;
- Official definition: A higher-order component is a function whose parameters are components and whose return value is a new component ;
We can perform the following analysis:
- First of all, a higher-order component itself is not a component , but a function ;
- Secondly, the parameter of this function is a component , and the return value is also a component ;
The calling process of higher-order components is similar to this:
const EnhancedComponent = higherOrderComponent(WrappedComponent);
The writing process of higher-order functions is similar to this:
function higherOrderComponent(WrapperComponent) {
return class NewComponent extends PureComponent {
render() {
return <WrapperComponent/>
}
}
}
Component name issue:
- In ES6, the class name can be omitted in class expressions, so it can be written as follows:
function higherOrderComponent(WrapperComponent) {
return class extends PureComponent {
render() {
return <WrapperComponent/>
}
}
}
- In addition, the name of the component can be
displayName
modified through:
The complete code can be written like this:
import React, {
PureComponent } from 'react';
function higherOrderComponent(WrapperComponent) {
return class NewComponent extends PureComponent {
render() {
return <WrapperComponent/>
}
}
}
class App extends PureComponent {
render() {
return (
<div>
App
</div>
)
}
}
export default higherOrderComponent(App);
Higher-order components are not part of the React API. They are design patterns based on the composition features of React ;
Higher-order components are very common in some React third-party libraries:
- For example, connect in redux;
- For
react-router
examplewithRouter
;
What can high-order components help us do in our development?
1.2. Use of higher-order components
1.2.1. Enhancement of props
Add new ones without modifying the original codeprops
Suppose we have the following case:
class Header extends PureComponent {
render() {
const {
name, age } = this.props;
return <h2>Header {
name + age}</h2>
}
}
export default class App extends PureComponent {
render() {
return (
<div>
<Header name="aaa" age={
18} />
</div>
)
}
}
We can use a higher-order component to allow users to enhance a component without destroying the original structure props
:
function enhanceProps(WrapperCpn, otherProps) {
return props => <WrapperCpn {
...props} {
...otherProps} />
}
const EnhanceHeader = enhanceProps(Header, {
height: 1.88})
- A bit like interceptor and java dynamic proxy
Use higher-order components to share Context properties
import React, {
PureComponent, createContext } from 'react';
const UserContext = createContext({
nickname: "默认",
level: -1
})
function Header(props) {
return (
<UserContext.Consumer>
{
value => {
const {
nickname, level } = value;
return <h2>Header {
"昵称:" + nickname + "等级" + level}</h2>
}
}
</UserContext.Consumer>
)
}
function Footer(props) {
return (
<UserContext.Consumer>
{
value => {
const {
nickname, level } = value;
return <h2>Footer {
"昵称:" + nickname + "等级" + level}</h2>
}
}
</UserContext.Consumer>
)
}
const EnhanceHeader = enhanceProps(Header, {
height: 1.88 })
export default class App extends PureComponent {
render() {
return (
<div>
<UserContext.Provider value={
{
nickname: "why", level: 90 }}>
<Header />
<Footer />
</UserContext.Provider>
</div>
)
}
}
Take advantage of higher-order components withUser
:
import React, {
PureComponent, createContext } from 'react';
const UserContext = createContext({
nickname: "默认",
level: -1
})
function withUser(WrapperCpn) {
return props => {
return (
<UserContext.Consumer>
{
value => {
return <WrapperCpn {
...props} {
...value}/>
}
}
</UserContext.Consumer>
)
}
}
function Header(props) {
const {
nickname, level } = props;
return <h2>Header {
"昵称:" + nickname + "等级:" + level}</h2>
}
function Footer(props) {
const {
nickname, level } = props;
return <h2>Footer {
"昵称:" + nickname + "等级:" + level}</h2>
}
const UserHeader = withUser(Header);
const UserFooter = withUser(Footer);
export default class App extends PureComponent {
render() {
return (
<div>
<UserContext.Provider value={
{
nickname: "why", level: 90 }}>
<UserHeader />
<UserFooter />
</UserContext.Provider>
</div>
)
}
}
1.2.2. Use high-order components for authentication judgment
During development, we may encounter scenarios like this:
- Some pages require the user to successfully log in before they can enter;
- If the user fails to log in successfully, jump directly to the login page;
At this time, we can use high-order components to complete the authentication operation:
function LoginPage() {
return <h2>LoginPage</h2>
}
function CartPage() {
return <h2>CartPage</h2>
}
export default class App extends PureComponent {
render() {
return (
<div>
<CartPage/>
</div>
)
}
}
Write high-level components for authentication:
function loginAuth(Page) {
return props => {
if (props.isLogin) {
return <Page/>
} else {
return <LoginPage/>
}
}
}
The complete code is as follows:
import React, {
PureComponent } from 'react';
function loginAuth(Page) {
return props => {
if (props.isLogin) {
return <Page/>
} else {
return <LoginPage/>
}
}
}
function LoginPage() {
return <h2>LoginPage</h2>
}
function CartPage() {
return <h2>CartPage</h2>
}
const AuthCartPage = loginAuth(CartPage);
export default class App extends PureComponent {
render() {
return (
<div>
<AuthCartPage isLogin={
true}/>
</div>
)
}
}
1.2.3. Life cycle hijacking
import React, {
PureComponent } from 'react';
class Home extends PureComponent {
UNSAFE_componentWillMount() {
this.begin = Date.now();
}
componentDidMount() {
this.end = Date.now();
const interval = this.end - this.begin;
console.log(`Home渲染使用时间:${
interval}`)
}
render() {
return (
<div>
<h2>Home</h2>
<p>我是home的元素,哈哈哈</p>
</div>
)
}
}
class Detail extends PureComponent {
UNSAFE_componentWillMount() {
this.begin = Date.now();
}
componentDidMount() {
this.end = Date.now();
const interval = this.end - this.begin;
console.log(`Detail渲染使用时间:${
interval}`)
}
render() {
return (
<div>
<h2>Detail</h2>
<p>我是detail的元素,哈哈哈</p>
</div>
)
}
}
export default class App extends PureComponent {
render() {
return (
<div>
<Home/>
<Detail/>
</div>
)
}
}
We can define higher-order components as follows:
function logRenderTime(WrapperCpn) {
return class extends PureComponent {
UNSAFE_componentWillMount() {
this.begin = Date.now();
}
componentDidMount() {
this.end = Date.now();
const interval = this.end - this.begin;
console.log(`Home渲染使用时间:${
interval}`)
}
render() {
return <WrapperCpn {
...this.props}/>
}
}
}
const LogHome = logRenderTime(Home);
const LogDetail = logRenderTime(Detail);
The complete code is as follows:
import React, {
PureComponent } from 'react';
function logRenderTime(WrapperCpn) {
return class extends PureComponent {
UNSAFE_componentWillMount() {
this.begin = Date.now();
}
componentDidMount() {
this.end = Date.now();
const interval = this.end - this.begin;
console.log(`${
WrapperCpn.name}渲染使用时间:${
interval}`)
}
render() {
return <WrapperCpn {
...this.props}/>
}
}
}
class Home extends PureComponent {
render() {
return (
<div>
<h2>Home</h2>
<p>我是home的元素,哈哈哈</p>
</div>
)
}
}
class Detail extends PureComponent {
render() {
return (
<div>
<h2>Detail</h2>
<p>我是detail的元素,哈哈哈</p>
</div>
)
}
}
const LogHome = logRenderTime(Home);
const LogDetail = logRenderTime(Detail);
export default class App extends PureComponent {
render() {
return (
<div>
<LogHome />
<LogDetail />
</div>
)
}
}
This way of writing is actually to extract common code for reuse .
1.3. The meaning of higher-order functions
We will find that using higher-order components can handle some React code more elegantly.
In fact, early React provided a way to reuse components mixin
, but it is no longer recommended to use:
Mixin
May be mutually dependent and coupled, which is not conducive to code maintenance- Methods in different ones
Mixin
may conflict with each other Mixin
Very often, components are perceptible and even have to be processed, which can cause snowballing complexity to the code.
Of course, HOC also has its own shortcomings:
- HOC needs to be wrapped or nested on the original component. If HOC is used extensively , a lot of nesting will be generated, which makes debugging very difficult;
- HOC can be hijacked
props
and may cause conflicts if the agreement is not followed;
The emergence of Hooks is groundbreaking. It solves many problems that existed before React, such as this
pointing problems, nesting complexity problems of HOC , etc.