React 組件解耦之道

React 的組件很是的靈活可擴展,不過隨着業務複雜度的增長和許多外部工具庫的引入,組件每每也會顯得浮腫,接下來咱們就一塊兒來看看常見的幾種,遵循單一職責原則的,組件分割與解耦的方法javascript

分割 render 函數

當一個組件渲染的內容較多時,有一個快速而且通用的方法是建立sub-render函數來簡化原來龐大的 renderjava

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

  renderBody() {
    // ...
  }

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

爲了再次簡化sub-render函數,咱們還能夠採用Functional Components寫法,這種方式生成了更小的處理單元,且更有利於測試node

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

用 props 傳遞元素

若是一個組件的狀態或配置較多,咱們能夠運用props傳遞元素而不只是數據,好比再聲明一個組件,使其中的父組件只專一於配置app

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

父組件函數

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} />;
  }
}

使用高階組件

實現點擊某組件的超連接,發送該組件的 ID,咱們大多的解決方法可能以下工具

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 <a> elements
      sendAnalytics('link clicked', {
        documentId: this.props.documentId // Specific information to be sent
      });
    }
  };

  render() {
    // ...
  }
}

然而它卻存在代碼不能複用組件重構困難等問題測試

咱們可使用高階組件來解決這些問題,顧名思義,高階組件就是一個函數,傳給它一個組件,它返回一個新的組件this

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 <a> elements
        const data = mapPropsToData ? mapPropsToData(this.props) : {};
        sendAnalytics('link clicked', data);
      }
    };

    render() {
      // Simply render the WrappedComponent with all props
      return <WrappedComponent {...this.props} />;
    }
  }

  return LinkAnalyticsWrapper;
}

簡化代碼以下spa

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

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

總結

以上 3 個 React 組件的解耦重構方法均可以直接拿來運用,最開始可能會以爲有點棘手,可是不要緊,只要堅持下來,你就會寫出更強壯和可複用的代碼code

原文連接: Techniques for decomposing React components (David Tang)

相關文章
相關標籤/搜索