[译]React 组件解耦之道

gl8661 7年前
   <p>React 的组件非常的 灵活可扩展 ,不过随着业务复杂度的增加和许多外部工具库的引入,组件往往也会显得 浮肿 ,接下来我们就一起来看看常见的几种,遵循 单一职责原则 的,组件 分割与解耦 的方法</p>    <h2>分割 render 函数</h2>    <p>当一个组件渲染的内容较多时,有一个快速并且通用的方法是创建 sub-render 函数来简化原来庞大的 render</p>    <pre>  <code class="language-javascript">class Panel extends React.Component {    renderHeading() {      // ...    }      renderBody() {      // ...    }      render() {      return (        <div>          {this.renderHeading()}          {this.renderBody()}        </div>      );    }  }</code></pre>    <p>为了再次简化 sub-render 函数,我们还可以采用 Functional Components 写法,这种方式生成了更小的处理单元,且更有利于测试</p>    <pre>  <code class="language-javascript">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>      );    }  }</code></pre>    <h2>用 props 传递元素</h2>    <p>如果一个组件的状态或配置较多,我们可以运用 props 传递元素而不仅是数据,比如再声明一个组件,使其中的父组件 只专注于配置</p>    <pre>  <code class="language-javascript">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>      );    }  }</code></pre>    <p>父组件</p>    <pre>  <code class="language-javascript">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} />;    }  }</code></pre>    <h2>使用高阶组件</h2>    <p>实现点击某组件的超链接,发送该组件的 ID,我们大多的解决方法可能如下</p>    <pre>  <code class="language-javascript">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() {      // ...    }  }</code></pre>    <p>然而它却存在 代码不能复用 , 组件重构困难 等问题</p>    <p>我们可以使用 高阶组件 来解决这些问题,顾名思义,高阶组件就是一个函数,传给它一个组件,它返回一个新的组件</p>    <pre>  <code class="language-javascript">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;  }</code></pre>    <p>简化代码如下</p>    <pre>  <code class="language-javascript">class Document extends React.Component {    render() {      // ...    }  }    export default withLinkAnalytics((props) => ({    documentId: props.documentId  }), Document);</code></pre>    <h2>总结</h2>    <p>以上 3 个 React 组件的 解耦重构 方法都可以直接拿来运用,最开始可能会觉得有点棘手,但是没关系,只要坚持下来,你就会写出更强大和可复用的代码</p>    <p> </p>    <p>来自:https://segmentfault.com/a/1190000010051000</p>    <p> </p>