Introduction: In the process of React project development, how to reduce code redundancy, provide code quality, and enhance code maintainability are all problems that we often have to consider. Next, I will use HOC, Render Props, and Hook to demonstrate some common component encapsulation techniques

I. HOC (High-level Component)

HOC is an advanced technique used in React to reuse component logic. HOC itself is not part of the React API; it is a design pattern based on the composite features of React.

If you haven’t seen HOC in detail before, you may feel unfamiliar with the term and think you haven’t used it before. In fact, HOC has already been widely used in your projects, but you just haven’t paid attention to it. I’m sure you’ve written the following code

import { connect } from 'react-redux'Const HocDemoComponent = connect(mapStateToProps, mapDispatchToProps)(DemoComponent) // React-redux connect returns a high-order groupCopy the code

As you can see from the code above, a higher-order component is a function that takes a component and returns a new component.

To take a simple example, let’s say we have a number of components that use the current mouse position. As a general practice, we register the method to get the mouse position in each component. In this case, the code is redundant and not easy to maintain.

Let’s implement it in HOC mode.

Mousepoint.js // a simple display of location information

import React from 'react'
import mousePositionHoc from '.. /hoc/MousePosition'
class MousePoint extends React.Component{
  constructor(props){
    super(props)
  }
  render() {return(
      <div>
        <span>鼠标的横坐标{this.props.positionX}</span>
        <span>鼠标的纵坐标{this.props.positionY}</span>
      </div>
    )
  }
}
export default mousePositionHoc(MousePoint)Copy the code

This is a simple component that shows the vertical and horizontal coordinates of the mouse, and the props parameter is derived from our HOC wrapper function.

Mouseposition.js // HOC encapsulates the function

import React from 'react'
export default (Component) => {
 return class WrappedComponent extends React.Component {
    constructor(props){
      super(props)
      this.state = {
        positionX: 0,
        positionY: 0
      }
    }
    componentDidMount() {
      document.addEventListener('mousemove', (e) => { this.setState({ positionX: e.clientX, positionY: E.clienty})}) // Here we update the mouse position, store it in the state, and pass it to the component being passed in via props}render() {return( <Component {... this.props} {... /> //props: This. State}/> //props: This returns the WrappedComponent, so we should pass it to the Component as WrappedComponent. WrappedComponent can manipulate its own states, and we can pass those states to the Component as props)}}}Copy the code

We don’t need to write the associated event bindings multiple times in componentDidMount if our other components want to use the values of mouse coordinates. Just mousePosition(Component).

This is how a higher-order component is implemented. Hold fast to the notion that a higher-order component is really a function that takes a component and returns a new component.

Higher-order components can be used in many scenarios, such as printing logs, extracting public functions, calling public apis, rendering public UIs, and so on.

Second, the Render Props

React is a simple technique for using prop functions to share code between React components

The React Router uses Render Props

<Route path="/home" render={() => <div>Home</div>} />Copy the code

For the same logic of mouse position information, we used Render Props as follows:

import React from 'react'
import MousePoint from './MousePoint'
export default class MouseTracker extends React.Component{
  constructor(props){
    super(props)
  }
  render() {return(
      <div>
        <MousePoint
          render={(state) => {
            return(< div > < span > abscissa mouse is {state. PositionX} < / span > < span > mouse ordinate is {state. PositionY} < / span > < / div >)}} / > < / div >)}}Copy the code

Here we render a MousePoint component that accepts a render props, which is not a simple property or object, but a function.

import React from 'react'
export default class MousePoint extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      positionX: 0,
      positionY: 0
    };
  }
  componentDidMount() {
    document.addEventListener('mousemove', (e) => {
      this.setState({
        positionX: e.clientX,
        positionY: e.clientY
      })
    })
  }
  
  render() {
    return( <div> { this.props.render(this.state) } </div> ); }}Copy the code

In the MousePoint component we are going to execute the render props, this.state as the parameter, and the function is going to render the mouse position successfully.

In React, props can pass any object, including components and functions. Through this free combination approach, we can achieve extraordinary functionality.

Speaking of which, we have to say this. Props. Children

import React from 'react'
export default class ChildComponent extends React.Component{
  constructor(props){
    super(props)
  }
  render() {return(
      <div>
        {this.props.children}
        {this.props.render}
      </div>
    )
  }
}


function App() {
  return (
    <div className="App">
        <ChildComponent render={<p>This is a message</p>}>
          <p>Hello World</p>
        </ChildComponent>
    </div>
  );
}Copy the code

This is also one of the ways we can encapsulate code, but it’s usually used for style encapsulation.

This.props. Children refers to the part of the component that is included between the start tag and end tag. We can also pass components as props, like render in the code above. This is similar to slot in Vue.

Third, the hooks

Finally, we use Hook to achieve the mouse position logic again

Official definition:

Hook

React 16.8 introduces the concept of hooks to make functional components have the same functionality as class components.

Let’s customize a Hook first

import React, { useState, useEffect } from 'react'
export default () => {
  const [positionX, setPositionX] = useState(0)
  const [positionY, setPositionY] = useState(0)
  const getMousePosition = (e) => {
    setPositionX(e.clientX)
    setPositionY(e.clientY)
  }
  useEffect(() => {
    document.addEventListener('mousemove', getMousePosition)
    return () => {
      document.removeEventListener('mousemove', getMousePosition)
    };
  });
  return {
    positionX: positionX,
    positionY: positionY
  }
}Copy the code

import React, {
  useState,
  useEffect
} from 'react'
import useMousePosition from '.. /hooks/useMouse'




export default () => {
  const mousePosition = useMousePosition()
  return{mousePosition.positionX}</span> <span> < mousePositiony}</span> </div>Copy the code

From the perspective of development, Hook is undoubtedly a common way for the industry in the future. Writing logic is clear, reuse convenient, functional components, abandon the class of this troublesome implementation. After all, it has just come out, and it will take some time to achieve widespread adoption. Therefore, if you are not familiar with Hook, don’t be too alarmed. If you are interested, you can go to the official website to learn about relevant knowledge. I just do a simple example here, and there will be some Hook related articles for detailed discussion.

These are common component encapsulation techniques used in React. Does knowing this make you want to refactor your code?