7
头图

join us!

160eebcb1ecf7b " , to provide front-end developers with technical information and a series of basic articles. For a better user experience, please move to our official website rookies (160eebcb1ecf83 https://xhs-rookies.com/ ) to learn and get the latest articles in time.

"Code tailor" , if you are interested in our article or want to make some suggestions, follow Mountain" public account, contact us, you can also watch it on WeChat Our article. Every suggestion or approval is a great encouragement to us!

Preface

In this section, we will introduce the React , what are the high-end components, and the supplements to the high-end components.

This article will introduce you to the following:

  • Recognize high-end components
  • Use of high-end components
  • Significance of high-end components
  • Points to note for high-end components
  • refs high-end components
  • Portals
  • Fragment
  • Strict mode- StrictMode

High-end components

Recognize high-end components

What are high-end components? I believe many students have heard of and used higher-order function , they are very similar, so we can first review what is higher-order function .

Wikipedia definition of higher-order function: at least one of the following conditions is met:

  • Accept one or more functions as input;
  • Output a function;

JavaScript more common filter , map , reduce are all higher-order functions.

So what are high-end components?

  • Higher-order components in English is Higher-Order Components , abbreviated as HOC , is an advanced technique for multiplexing component logic in React
  • Official definition: parameter is the component and the return value is the new component;

From this, we can analyze:

  • high-order component itself is not a component, but a function
  • The parameter of this function is a component, and the return value is also a component

The calling process of high-level components is similar to this:

const EnhancedComponent = higherOrderComponent(WrappedComponent)

The component is to transform props into UI, and the higher-order component is to transform the component into another component.

The writing process of higher-order functions is similar to this:

  • Return class components, suitable for stateful processing and life cycle requirements
function higherOrderComponent(WrapperComponent) {
  return class NewComponent extends PureComponent {
    render() {
      return <WrapperComponent />
    }
  }
}
  • Return function component, suitable for simple logic processing
function higherOrderComponent(WrapperComponent) {
  return (props) => {
    if (props.token) {
      return <WrapperComponent />
    } else {
      return <></>
    }
  }
}

In ES6, the class name in the class expression can be omitted, so there is the following way of writing:

function higherOrderComponent(WrapperComponent) {
  return class extends PureComponent {
    render() {
      return <WrapperComponent />
    }
  }
}

The component name can be modified displayName

function higherOrderComponent(WrapperComponent) {
  class NewComponent extends PureComponent {
    render() {
      return <WrapperComponent />
    }
  }
  NewComponent.displayName = 'xhsRookies'
  return NewComponent
}
Note: high-end components are not React API , it is a design pattern formed React

So, in our development, what can high-end components help us do? Look down!

Use of high-end components

enhancement of props

1. Without modifying the original code, add a new props attribute

Suppose we have the following case:

class XhsRookies extends PureComponent {
  render() {
    const { name, age } = this.props
    return <h2>XhsRookies {name + age}</h2>
  }
}

export default class App extends PureComponent {
  render() {
    return (
      <div>
        <XhsRookies name="xhsRookies" age={18} />
      </div>
    )
  }
}

We can pass a high-level components, without destroying the original props the case of, for enhanced components, if need XhsRookies components props add a height property, we can do this:

class XhsRookies extends PureComponent {
  render() {
    const { name, age } = this.props
    return <h2>XhsRookies {name + age}</h2>
  }
}

function enhanceProps(WrapperComponent, newProps) {
  return (props) => <WrapperComponent {...props} {...newProps} />
}

const EnhanceHeader = enhanceProps(XhsRookies, { height: 1.88 })

export default class App extends PureComponent {
  render() {
    return (
      <div>
        <EnhanceHeader name="xhsRookies" age={18} />
      </div>
    )
  }
}

Use high-level components to share Context

import React, { PureComponent, createContext } from 'react'

const UserContext = createContext({
  nickname: '默认',
  level: -1,
})

function XhsRookies(props) {
  return (
    <UserContext.Consumer>
      {(value) => {
        const { nickname, level } = value
        return <h2>Header {'昵称:' + nickname + '等级' + level}</h2>
      }}
    </UserContext.Consumer>
  )
}

export default class App extends PureComponent {
  render() {
    return (
      <div>
        <UserContext.Provider value={{ nickname: 'xhsRookies', level: 99 }}>
          <XhsRookies />
        </UserContext.Provider>
      </div>
    )
  }
}

We define a high-level component ShareContextHOC to share context

import React, { PureComponent, createContext } from 'react'

const UserContext = createContext({
  nickname: '默认',
  level: -1,
})

function ShareContextHOC(WrapperCpn) {
  return (props) => {
    return (
      <UserContext.Consumer>
        {(value) => {
          return <WrapperCpn {...props} {...value} />
        }}
      </UserContext.Consumer>
    )
  }
}

function XhsRookies(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 NewXhsRookies = ShareContextHOC(Header)

export default class App extends PureComponent {
  render() {
    return (
      <div>
        <UserContext.Provider value={{ nickname: 'xhsRookies', level: 99 }}>
          <NewXhsRookies />
        </UserContext.Provider>
      </div>
    )
  }
}

Rendering judgment authentication

During development, we will encounter the following scenarios:

  • Some pages must be successfully logged in to enter
  • If the user does not log in successfully, jump directly to the login page

In this scenario, we can use high-level components to complete the authentication operation:

function LoginPage() {
  // 登录页面
  return <h2>LoginPage</h2>
}

function HomePage() {
  // 登录成功可访问页面
  return <h2>HomePage</h2>
}

export default class App extends PureComponent {
  render() {
    return (
      <div>
        <HomePage />
      </div>
    )
  }
}

Use authentication components:

import React, { PureComponent } from 'react'

function loginAuthority(Page) {
  return (props) => {
    if (props.isLogin) {
      // 如果登录成功 返回成功页面
      return <Page />
    } else {
      // 如果为登录成功 返回登录页面
      return <LoginPage />
    }
  }
}

function LoginPage() {
  return <h2>LoginPage</h2>
}

function HomePage() {
  return <h2>HomePage</h2>
}

const AuthorityPassPage = loginAuthority(HomePage)

export default class App extends PureComponent {
  render() {
    return (
      <div>
        <AuthorityPassPage isLogin={true} />
      </div>
    )
  }
}

Life cycle hijacking

When multiple components need to do something in the life cycle, and these things are all the same logic, we can use high-level components to help these components in a unified way to complete these tasks, as shown in the following example:

import React, { PureComponent } from 'react'

class Home extends PureComponent {
  componentDidMount() {
    const nowTime = Date.now()
    console.log(`Home渲染使用时间:${nowTime}`)
  }

  render() {
    return (
      <div>
        <h2>Home</h2>
        <p>我是home的元素,哈哈哈</p>
      </div>
    )
  }
}

class Detail extends PureComponent {
  componentDidMount() {
    const nowTime = Date.now()
    console.log(`Detail渲染使用时间:${nowTime}`)
  }

  render() {
    return (
      <div>
        <h2>Detail</h2>
        <p>我是detail的元素,哈哈哈</p>
      </div>
    )
  }
}

export default class App extends PureComponent {
  render() {
    return (
      <div>
        <Home />
        <Detail />
      </div>
    )
  }
}

We can use high-end home prices to help complete the componentDidMount life cycle function of the 060eebcb1ed5ee component and the detail

import React, { PureComponent } from 'react'

function logRenderTime(WrapperCpn) {
  return class extends PureComponent {
    componentDidMount() {
      const nowTime = Date.now()
      console.log(`${WrapperCpn.name}渲染使用时间:${nowTime}`)
    }

    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>
    )
  }
}

Significance of high-end components

Through the use of high-level components in the above different situations, we can find that using high-level components can perform more elegant processing React

In fact, the early React provided a way to reuse components between mixin , which is no longer recommended:

  • Mixin may be mutually dependent and coupled, which is not conducive to code maintenance
  • The methods in different Mixin may conflict with each other
  • Mixin is very much, the component can be perceived, and even related processing is required for it, which will cause snowball complexity to the code

Of course, HOC also has some shortcomings of its own:

  • HOC needs to be wrapped or nested on the original components. If a large number of HOC used, there will be a lot of nesting, which makes debugging very difficult;
  • HOC can hijack props , and it may cause conflicts if the agreement is not complied with;

Reasonable use of high-end components will be of great help to our development.

Points to note for high-end components

Don't use HOC in the render method

React of diff algorithm (called coordination) using the identifier to determine whether it is component should be updated prior subtree or discarded and mount a new subtree. If render is the same as the component in the previous rendering ( === ), then React recursively updates the subtree by distinguishing the subtree from the new subtree. If they are not equal, the previous subtree is completely unloaded.

Normally, you don't need to think about this. But HOC , because it means that you should not HOC render method of the component:

render() {
  // 每次调用 render 函数都会创建一个新的 EnhancedComponent
  // EnhancedComponent1 !== EnhancedComponent2
  const EnhancedComponent = enhance(MyComponent);
  // 这将导致子树每次渲染都会进行卸载,和重新挂载的操作!
  return <EnhancedComponent />;
}

This is not just a performance issue-remounting a component will cause the state of the component and all its subcomponents to be lost.

HOC outside of the component, the component will only be created once. Therefore, it will be the same component render Generally speaking, this is consistent with your expected performance.

const EnhancedComponent = enhance(MyComponent)

class App extends PureComponent {
  render() {
    return <EnhancedComponent />
  }
}

In rare cases, you need to call HOC dynamically. You can call it in the life cycle method of the component or its constructor.

refs will not be passed

Although the agreement of higher-order components is to pass all props to the packaged component, this does not apply refs That's because ref is not actually a prop , just like key , it is specially processed React If ref added to HOC return assembly, the ref references to the container assembly, rather than packaging assembly.

Supplement of components

Forward refs in high-end components

Earlier we mentioned that in high-end components, refs will not be passed, but we may encounter the need to forward refs in high-end components during development, so how do we solve it? Fortunately, we can use the React.forwardRef API to help solve this problem.

Let's start with an output component props to the console HOC start Example:

function logProps(WrappedComponent) {
  class LogProps extends React.Component {
    componentDidUpdate(prevProps) {
      console.log('old props:', prevProps)
      console.log('new props:', this.props)
    }

    render() {
      return <WrappedComponent {...this.props} />
    }
  }

  return LogProps
}

logProps HOC penetrates all props to its packaged components, so the rendering result will be the same. For example: we can use this HOC record all passed to the " fancy button " component props :

class FancyButton extends React.Component {
  focus() {
    // ...
  }

  // ...
}

// 我们导出 LogProps,而不是 FancyButton。
// 虽然它也会渲染一个 FancyButton。
export default logProps(FancyButton)

refs to now, this example is as mentioned earlier, 060eebcb1ed9a4 will not be passed through. If you HOC add ref , the ref references outermost container component, not the component being wrapped.

import FancyButton from './FancyButton'

const ref = React.createRef()

// 我们导入的 FancyButton 组件是高阶组件(HOC)LogProps。
// 尽管渲染结果将是一样的,
// 但我们的 ref 将指向 LogProps 而不是内部的 FancyButton 组件!
// 这意味着我们不能调用例如 ref.current.focus() 这样的方法
;<FancyButton label="Click Me" handleClick={handleClick} ref={ref} />

At this time, we can use the React.forwardRef API to explicitly refs to the internal FancyButton component. React.forwardRef accepts a rendering function, which receives props and ref parameters and returns a React node.

function logProps(Component) {
  class LogProps extends React.Component {
    componentDidUpdate(prevProps) {
      console.log('old props:', prevProps)
      console.log('new props:', this.props)
    }

    render() {
      const { forwardedRef, ...rest } = this.props

      // 将自定义的 prop 属性 “forwardedRef” 定义为 ref
      return <Component ref={forwardedRef} {...rest} />
    }
  }

  // 注意 React.forwardRef 回调的第二个参数 “ref”。
  // 我们可以将其作为常规 prop 属性传递给 LogProps,例如 “forwardedRef”
  // 然后它就可以被挂载到被 LogProps 包裹的子组件上。
  return React.forwardRef((props, ref) => {
    return <LogProps {...props} forwardedRef={ref} />
  })
}

In this way, we can pass refs in high-level components.

Portals

In some cases, the content we want to render is independent of the parent component, or even independent of the currently mounted DOM element (the default is mounted on the DOM element with root

Portal provides an excellent solution for DOM node that exists outside the parent component:

  • The first parameter ( child ) is any renderable React child element, such as an element, string or fragment ;
  • The second parameter ( container ) is a DOM element;
ReactDOM.createPortal(child, container)

Generally speaking, when you render method of the component, the element will be mounted to the nearest parent node of the DOM

render() {
  // React 挂载了一个新的 div,并且把子元素渲染其中
  return (
    <div>
      {this.props.children}
    </div>
  );
}

However, sometimes it is beneficial to insert child elements into different positions in the DOM node:

render() {
  // React 并*没有*创建一个新的 div。它只是把子元素渲染到 `domNode` 中。
  // `domNode` 是一个可以在任何位置的有效 DOM 节点。
  return ReactDOM.createPortal(
    this.props.children,
    domNode
  );
}

For example, we are going to develop a TabBar component, which can render its child components to the top position of the screen:

  • The first step: modify index.html add a new node
<div id="root"></div>
<!-- 新节点 -->
<div id="TabBar"></div>
  • Step 2: Write the style of this node
#TabBar {
  position: fixed;
  width: 100%;
  height: 44px;
  background-color: red;
}
  • Step 3: Write component code
import React, { PureComponent } from 'react'
import ReactDOM from 'react-dom'

class TabBar extends PureComponent {
  constructor(props) {
    super(props)
  }

  render() {
    return ReactDOM.createPortal(this.props.children, document.getElementById('TabBar'))
  }
}

export default class App extends PureComponent {
  render() {
    return (
      <div>
        <TabBar>
          <button>按钮1</button>
          <button>按钮2</button>
          <button>按钮3</button>
          <button>按钮4</button>
        </TabBar>
      </div>
    )
  }
}

Fragment

In previous developments, we always wrapped a div element when returning content in a component:

export default class App extends PureComponent {
  render() {
    return (
      <div>
        <h2>微信公众号:小和山的菜鸟们</h2>
        <button>点赞</button>
        <button>关注</button>
      </div>
    )
  }
}

Rendering result

7FB293B8-6095-44E9-B80E-1A2D1B3B90AF.png

We will find an div element:

  • This div element is needed for certain scenarios (for example, we want to put it in a div element, and then set the style specifically)
  • In some scenarios, this div is not necessary. For example, at present, I may want all the content to be directly rendered into root ;

When we delete this div , an error will be reported. What should we do if we do not want to render this div ?

  • Use Fragment
  • Fragment allows you to group DOM without adding additional nodes to 060eebcb1edea4;
export default class App extends PureComponent {
  render() {
    return (
      <Fragment>
        <h2>微信公众号:小和山的菜鸟们</h2>
        <button>点赞</button>
        <button>关注</button>
      </Fragment>
    )
  }
}

The rendering effect is as follows:

image.png

React also provides Fragment

It looks like empty label <></>

export default class App extends PureComponent {
  render() {
    return (
      <>
        <h2>微信公众号:小和山的菜鸟们</h2>
        <button>点赞</button>
        <button>关注</button>
      </>
    )
  }
}
Note: if we need Fragment add attributes, such as key , we can not use the grammar section

Strict Mode-StrictMode

StrictMode is a tool used to highlight potential problems in the application. Like Fragment , StrictMode does not render any visible UI. It triggers additional checks and warnings for its descendant elements.

Note: strict mode checks only run in development mode; they will not affect production builds.

You can enable strict mode for any part of the application. E.g:

import React from 'react'

function ExampleApplication() {
  return (
    <div>
      <Header />
      <React.StrictMode>
        <div>
          <ComponentOne />
          <ComponentTwo />
        </div>
      </React.StrictMode>
      <Footer />
    </div>
  )
}

In the above example, will run strict mode checks on the Header and Footer However, ComponentOne and ComponentTwo and all their descendant elements will be checked.

StrictMode currently helps:

  • Identify unsafe life cycles
  • Warning about using the obsolete findDOMNode
  • Detect unexpected side effects
  • Detect outdated context API
  • Warning about using the obsolete string ref API

1. Identify the unsafe life cycle

Certain outdated lifecycle methods are not safe to use React However, if your application uses third-party libraries, it is difficult to ensure that they do not use these life cycle methods.

When strict mode is enabled, React class components that use the unsafe life cycle method, and print a warning message containing the information of these components, as shown below:

image.png


2. Warning about using outdated string ref API

Previously, React provided two ways to manage refs :

  • Obsolete string ref API form
  • The form of the callback function API

Despite a string ref API easier to use in both, but it has some disadvantages , therefore official recommended way callback .

React 16.3 adds a third option, which provides the ref without any disadvantages:

class MyComponent extends React.Component {
  constructor(props) {
    super(props)

    this.inputRef = React.createRef()
  }

  render() {
    return <input type="text" ref={this.inputRef} />
  }

  componentDidMount() {
    this.inputRef.current.focus()
  }
}

Since the object ref mainly added to replace the string ref , strict mode now warns of the use of the string ref .


3. Warning about using the obsolete findDOMNode method

React supports findDOMNode to search for DOM nodes in the tree with a given class instance. Usually you don't need to do this, because you can bind ref directly to DOM nodes. Since this method is obsolete, I won't go into details here. If you are interested, you can learn by yourself.


4. Detect unexpected side effects

  • constructor this component will be called twice;
  • This is a deliberate operation in strict mode, allowing you to see if some of the logic codes written here will be called multiple times, if there will be some side effects;
  • In a production environment, it will not be called twice;
class Home extends PureComponent {
  constructor(props) {
    super(props)

    console.log('home constructor')
  }

  UNSAFE_componentWillMount() {}

  render() {
    return <h2 ref="home">Home</h2>
  }
}

5. Detect outdated context API

Early Context by static property declaration Context object properties, by getChildContext return Context objects and other ways to use Context of; but at the moment this method is outdated, obsolete context API error-prone, will be removed in the next major version of. It is still valid in all 16.x versions, but in strict mode, the following warning will be displayed:

img.png

Preview of the next section

In this section, we have studied the React and the supplementary content of the components. In the next chapter, we will start a new study React-Router , so stay tuned!


小和山的菜鸟们
377 声望2.1k 粉丝

每日进步的菜鸟,分享前端学习手册,和有心学习前端技术的小伙伴们互相探讨,一同成长。