Reuse of component state logic: render props mode and higher-order components

Posted by abduljan on Mon, 27 Jan 2020 08:52:43 +0100

Article directory

1, Render props mode

1.1 overview of react component reuse

  • Think about this: what if some of the two components are functionally similar or identical?
  • Processing mode: reuse similar functions
  • Reuse what?
    • state
    • How to operate state
  • There are two ways:
    • render props mode
    • High level components (HOC)
  • Note: these two methods are not new API s, but fixed patterns evolved by using the coding skills of React

1.2 thinking analysis

  • Idea: encapsulate the state to be reused and the method to operate the state into a component

  • How to get the reused state in this component

    • When using components, add a prop whose value is function, and obtain it through function parameter
<App render={ (mouse)=> {} }/>
  • How to render to any UI

    • Use the return value of this function as the UI content to render
<App render={ (mouse) => {
	<p>Current mouse position{mouse.x},{mouse.y}</p>
} }/>

1.3 operation steps

  • Create a Mouse component to provide reused logic code in the component
  • Expose the state to be reused as a parameter of props.render(state) method outside the component
  • Use the return value of props.render() as the content to render

Example

import React from 'react'
import ReactDOM from 'react-dom'

class Mouse extends React.Component {
  // Mouse position status
  state = {
    x: 0,
    y: 0
  }
  // Listening for mouse movement events
  componentDidMount() {
    window.addEventListener('mousemove', this.handleMouseMove)
  }

  handleMouseMove = e => {
    this.setState({
      x: e.clientX,
      y: e.clientY
    })
  }

  render() {
    return this.props.render(this.state)
  }
}

class App extends React.Component {
  render() {
    return (
      <div>
        <Mouse
          render={mouse => {
            return (
              <p>
                X:{mouse.x},Y:{mouse.y}
              </p>
            )
          }}
        />
      </div>
    )
  }
}

ReactDOM.render(<App />, document.getElementById('root'))

1.4 children instead of the render attribute

  • Note: it is not necessary to use prop named render when the mode is called render props. In fact, you can use prop of any name
  • The technique of making prop a function and telling components what to render is called render props mode
  • Recommendation: use children instead of render
<Mouse>
{ {x,y} => <p>The mouse position is{x}{y}</p> }
</Mouse>

// Internal components
this.props.children(this.state)

1.5 code optimization

// Add props verification
Mouse.propTypes = {
	children: PropTypes.func.isRequired
}

//Clear events when uninstalling components
componentWillUnmount() {
	window.removeEventListener('mousemove', this.handleMouseMove)
}

2, High order components

2.1 overview

  • Purpose: to realize the reuse of state logic
  • Adopt packaging mode
  • Mobile phone: get protection
  • Mobile phone case: provides protection function
  • The high-level components are equivalent to the mobile phone shell. Through packaging components, the function of components is enhanced

2.2 thinking analysis

  • Higher order component (HOC, higher order component) is a function that receives the component to be wrapped and returns the enhanced component
const EnhancedComponent = withHOC(WrappedComponent)
  • A class component is created inside the high-level component, in which the reused state logic code is provided, and the reused state is passed to the wrapped component WrappedComponent through prop
// Class components created within higher-level components
class Mouse extends React.Component {
	render() {
		return <WrappedComponent {...this.state} />
	}
}

2.3 use steps

  • Create a function whose name convention starts with
  • Specifies a function parameter that should start with an uppercase letter
  • Create a class component inside the function, provide reusable status logic code, and return
  • In this component, the parameter component is rendered and the state is passed to the parameter component through prop
  • Call the higher-level component, pass in the component to be enhanced, get the enhanced component through the return value, and render it to the page
import React from 'react'
import ReactDOM from 'react-dom'

// Create function
function withMouse(WrappedComponent) {
  class Mouse extends React.Component {
    // Mouse position status
    state = {
      x: 0,
      y: 0
    }
    // Listen for mouse movement events when mounting
    componentDidMount() {
      window.addEventListener('mousemove', this.handleMouseMove)
    }

    // Unbind event when component is removed
    componentWillUnmount() {
      window.removeEventListener('mousemove', this.handleMouseMove)
    }

    handleMouseMove = e => {
      this.setState({
        x: e.clientX,
        y: e.clientY
      })
    }

    render() {
      // Return the passed component in the render function, and set the status of the current component
      return <WrappedComponent {...this.state} />
    }
  }
  return Mouse
}

function Position(props) {
  return (
    <p>
      X:{props.x}
      Y:{props.y}
    </p>
  )
}
// Wrap the position component
let MousePosition = withMouse(Position)

class App extends React.Component {
  render() {
    return (
      <div>
        //High order component
        // Render wrapper component
        <MousePosition />
      </div>
    )
  }
}

ReactDOM.render(<App />, document.getElementById('root'))

2.3 setting displayName

  • The problem with using higher-order components: get two components with the same names
  • Reason: by default, React uses the component name as the displayName
  • Solution: set displayName for high-level components, so that different components can be distinguished during debugging
  • The role of displayName: used to set debug information (React Developer Tools information)
  • Setting method:
Mouse.displayName = `WithMouse${getDisplayName(WrappedComponent)}`

function  getDisplayName(WrappedComponent) {
	return WrappedComponent.displayName || WrappedComponent.name || 'Component'
}

2.4 transfer props

  • Problem: if props are not passed, it will cause loss of props
  • Solution: when rendering WrappedComponent, pass state and props together to the component
<WrappedComponent {...this.state} {...this.props} />
Poi
Published 63 original articles, won praise 31, visited 6141
Private letter follow

Topics: React Mobile Attribute shell