[译]React 组件解耦之道

React   2017-07-05 20:02:55 发布
您的评价:
     
0.0
收藏     0收藏
文件夹
标签
(多个标签用逗号分隔)

React 的组件非常的 灵活可扩展 ,不过随着业务复杂度的增加和许多外部工具库的引入,组件往往也会显得 浮肿 ,接下来我们就一起来看看常见的几种,遵循 单一职责原则 的,组件 分割与解耦 的方法

分割 render 函数

当一个组件渲染的内容较多时,有一个快速并且通用的方法是创建 sub-render 函数来简化原来庞大的 render

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

  renderBody() {
    // ...
  }

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

为了再次简化 sub-render 函数,我们还可以采用 Functional Components 写法,这种方式生成了更小的处理单元,且更有利于测试

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 传递元素而不仅是数据,比如再声明一个组件,使其中的父组件 只专注于配置

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() {
    // ...
  }
}

然而它却存在 代码不能复用 , 组件重构困难 等问题

我们可以使用 高阶组件 来解决这些问题,顾名思义,高阶组件就是一个函数,传给它一个组件,它返回一个新的组件

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

简化代码如下

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

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

总结

以上 3 个 React 组件的 解耦重构 方法都可以直接拿来运用,最开始可能会觉得有点棘手,但是没关系,只要坚持下来,你就会写出更强大和可复用的代码

 

来自:https://segmentfault.com/a/1190000010051000

 

扩展阅读

『译』React Mixin 的使用
React.js生态系统概览 [译]
[译]在 React.js 中使用 ES6+
[译]如何架构一个 React 项目?
[译]推荐5个值得学习React Native的开源项目

为您推荐

react+redux教程(一)
ReactNative Redux Jest Parse.com/Openshift w/ Hapi & JWT Authentication Redis & Mongo
前端之React实战-交互与动态UI
野心勃勃的React组件生命周期
React 入门实例教程

更多

React
相关文档  — 更多
相关经验  — 更多
相关讨论  — 更多