I’ve shared a few articles about the React technology stack:

  • Uber mobile web version is not enough to create the ultimate performance to see the real chapter
  • Analyze the Twitter front-end architecture to learn about complex scenario data design
  • React + ES next = ♥
  • React+Redux creates “NEWS EARLY”, a one-page app that understands the true nature of the cutting-edge technology stack
  • React + Redux project example
  • .

Today we’ll discuss an interesting topic about React component design: several advanced ways to decompose the React component.

The React component is incredibly magical and flexible. We can play a lot with the design of the components. But ensuring the Single responsibility principle of components: The Single principle is important to make our components simpler, more maintainable, and more importantly reusable.

However, decomposing a complex and bloated React component may not be a straightforward task. This article introduces three methods for decomposing the React component.

Cut the Render () method

This is the easiest way to think about it: when a component renders many elements, you need to try to separate the rendering logic of those elements. The quickest way to do this is to split the Render () method into multiple sub-render methods.

It’s more intuitive to look at the following example:

class Panel extends React.Component {
  renderHeading() {
    // ...
  }

  renderBody() {
    // ...
  }

  render() {
    return (
      < div>
        {this.renderHeading()}
        {this.renderBody()}
      < /div>
    );
  }Copy the code

A careful reader will quickly notice that this does not decompose the component itself; the Panel component retains the original state, props, and class methods.

How do you really reduce complexity? We need to create some child components. In this case, it would be a good idea to use the functional/stateless components recommended in the latest React version:

const PanelHeader = (props) => ( // ... ) ; const PanelBody = (props) => ( // ... ) ; class Panel extends React.Component { render() { return ( < div> // Nice and explicit about which props are used < PanelHeader title={this.props.title}/> < PanelBody content={this.props.content}/> < /div> ); }}Copy the code

Compared with the previous approach, this subtle improvement is revolutionary. We created two new unit components: PanelHeader and PanelBody. This makes it easier to test different components separately. Meanwhile, with the help of React Fiber, a new algorithm engine for React, the rendering efficiency of the two unit components is optimistically expected to be greatly improved.

Template components

Back to the beginning of the question, why does a component become bloated and complex? There are many nested elements in the rendering, and there are many internal changes or multiple configurations in the component.

You can then transform the component into a template: The parent component is like a template and only focuses on configurations.

Again, let me give you an example, just to make it a little bit clearer.

Let’s say we have a Comment component that has multiple actions or events. At the same time, the information displayed by the component varies according to the user’s identity: whether the user is the author of this comment, whether the comment is saved correctly, and different permissions will lead to different display behaviors of this component. Instead of confusing all the logic together, it might be better to take advantage of React’s ability to pass React Elements from component to component, which is more like a template:

class CommentTemplate extends React.Component { static propTypes = { // Declare slots as type node metadata: PropTypes.node, actions: PropTypes.node, }; render() { return ( < div> < CommentHeading> < Avatar user={... }/> // Slot for metadata < span>{this.props.metadata}< /span> < /CommentHeading> < CommentBody/> < CommentFooter> < Timestamp time={... }/> // Slot for actions < span>{this.props.actions}< /span> < /CommentFooter> < /div> ...Copy the code

At this point, our real Comment component is organized as follows:

class Comment extends React.Component { render() { const metadata = this.props.publishTime ? < PublishTime time={this.props.publishTime} /> : < span>Saving... < /span>; const actions = []; if (this.props.isSignedIn) { actions.push(< LikeAction />); actions.push(< ReplyAction />); } if (this.props.isAuthor) { actions.push(< DeleteAction />); } return < CommentTemplate metadata={metadata} actions={actions} />; }Copy the code

Metadata and actions are just React elements that need to be rendered in certain situations.

For example, if this.props. PublishTime exists, metadata is < publishTime time={this.props. PublishTime} />; < span>Saving… < / span >.

If the user is logged in, render < LikeAction /> and < ReplyAction />, or add < DeleteAction /> to render content if the author is logged in.

High order component

In real development, components are often contaminated by other requirements.

For example, we want to count clicks on all links on a page. When the link is clicked, the statistical request is sent, and the ID value of the document of this page is included. A common practice is to add code logic to the Document component’s lifecycle functions componentDidMount and componentWillUnmount:

class Document extends React.Component {
  componentDidMount() {
    ReactDOM.findDOMNode(this).addEventListener('click', this.onClick);
  }

  componentWillUnmount() {
    ReactDOM.findDOMNode(this).removeEventListener('click', this.onClick);
  }

  onClick = (e) => {
    if (e.target.tagName === 'A') { // Naive check for elements sendAnalytics('link clicked', { documentId: this.props.documentId // Specific information to be sent }); }}; render() { // ...Copy the code

The problems with this are:

  • Related component Document in addition to its main logic: display the main page, more than other statistical logic;
  • If there were other logic in the lifecycle function of the Document component, the component would become even more ambiguous;
  • Statistical logic code cannot be reused;
  • Component refactoring and maintenance will become more difficult.

To solve this problem, we propose the concept of higher-order components (HOCs). Without trying to explain the term, let’s look directly at how the above code could be refactored using higher-order components:

function withLinkAnalytics(mapPropsToData, WrappedComponent) {
  class LinkAnalyticsWrapper extends React.Component {
    componentDidMount() {
      ReactDOM.findDOMNode(this).addEventListener('click', this.onClick);
    }

    componentWillUnmount() {
      ReactDOM.findDOMNode(this).removeEventListener('click', this.onClick);
    }

    onClick = (e) => {
      if (e.target.tagName === 'A') { // Naive check for elements const data = mapPropsToData ? mapPropsToData(this.props) : {}; sendAnalytics('link clicked', data); }}; render() { // Simply render the WrappedComponent with all props return < WrappedComponent {... this.props} />; }}Copy the code

Note that the withLinkAnalytics function does not change the WrappedComponent itself or the behavior of the WrappedComponent. Instead, it returns a wrapped new component. Practical usage:

class Document extends React.Component {
  render() {
    // ...
  }
}

export default withLinkAnalytics((props) => ({
  documentId: props.documentId
}), Document);Copy the code

This way, the Document component still only needs to care about its own parts, while withLinkAnalytics gives it the ability to reuse statistical logic.

The existence of higher-order components perfectly demonstrates the inherent compositional ability of React, which is widely used in the React community by react-Redux, Styled components, and React-Intl. It’s worth noting that the Recompose library does something “imaginative” by leveraging higher-order components.

conclusion

React and its surrounding communities have made functional programming all the rage. In my opinion, the thoughts about decomposture and composing are worth studying. At the same time, one piece of advice for development design is not to hesitate to break up your components into smaller, more monolithic components, as this is in return for robustness and reuse.

This article paraphrased Techniques for Decomposture React Components by David Tang.

Happy Coding!

PS: author Github warehouse, welcome to communicate through various forms of code.