您的位置:首页 » 分类: JavaScript & 前端资源 & ES2015 (ES6) & React » 文章: React 组件模式

React 组件模式

小编推荐:掘金是一个面向程序员的高质量技术社区,从 一线大厂经验分享到前端开发最佳实践,无论是入门还是进阶,来掘金你不会错过前端开发的任何一个技术干货。

我正在准备为一场聚会做技术分享,我想花点时间和大家分享一下我在 React 组件模式上学到的东西。Components(组件) 是 React 的核心,因此,了解如何利用它们对于构建优秀的设计结构至关重要。

所提供的示例受到了 Michael Chan 对 React 组件模式的精彩演讲 的极大启发。我强烈建议观看视频。

什么是组件

根据 reactjs.org 官网的说法,“组件(Components) 让你可以将用户界面分成独立的,可复用的小部件,并可以对每个部件进行单独的设计。”

当你第一次运行 npm install react 时,你得到了一个东西:组件及其API。与 JavaScript 函数类似,组件接受名为 “props” 的输入并返回 React 元素,它描述(声明)用户界面(UI)应该是什么样子。这就是为什么 React 被称为声明式 API 的原因,因为你告诉它你想要的 UI 看起来长什么样,剩下的事情 React 会帮你处理。

可以将声明式想象成你坐出租车去某个地方,你只需告诉司机你想去哪儿,然后他/她会实际驾驶到达目的地。而命令式编程恰好相反,你要亲自开车才能到达目的地。

组件 API

那么当你安装 React 时,得到的API到底是什么呢?有五个,他们是:

  • render
  • state
  • props
  • context
  • lifecycle events

虽然每个组件具有充分利用上述所有 API 的能力,但你会发现一些组件会使用部分 API ,而其他组件仅使用另一些 API 。这种情况可以将组件们划分为两类,称为 有状态(stateful) 组件 和 无状态(stateless) 组件 。有状态组件通常使用 有状态的(stateful) API:如render, state 和 lifecycle events,而无状态组件则使用 render, props 和 context。

现在我们要开始介绍组件模式了。组件模式是最佳的使用实践,并首先被引入来分割 数据或逻辑层 和 UI或表示层 。通过划分组件之间的职责,你可以创造更多可重复使用的,结合更加紧密的组件,以用来构建复杂的UI。在构建可扩展的应用程序时,这一点尤为重要。


组件模式

常见的组件模式是:

  • 容器(Container)组件
  • 展示(Presentational)组件
  • 高阶组件(Higher order components , HOC )
  • 渲染回调(Render callback)

容器(Container)组件

“容器组件就是执行数据提取,然后渲染其子组件而已” —— Jason Bonta

图注:蓝色表示容器(Container)组件,而灰色表示展示(Presentational)组件

容器组件是您的数据或逻辑层,并利用 有状态的(stateful) API 。使用生命周期事件,您可以连接到 Redux 或 Flux 等状态管理管理容器,并将数据和回调作为 props(属性) 传递给子组件。在容器组件的 render 方法中,您可以将展示子组件组合成 UI 。为了能够访问所有 有状态的(stateful) API,容器组件必须是 类(class)组件 而不是函数式组件。关于函数式组件和类组件的说明请参考官方文档

在下面的示例中,我们有一个名为 Greeting 的类组件,它具有 state(状态),生命周期事件 componentDidMount()render

class Greeting extends React.Component {
  constructor() {
    super();
    this.state = {
      name: "",
    };
  }

  componentDidMount() {
    // AJAX
    this.setState(() => {
      return {
        name: "William",
      };
    });
  }

  render() {
    return (
      <div>
        <h1>Hello! {this.state.name}</h1>
      </div>
    );
  }
}

此时,此组件是 有状态的(stateful) 类组件。为了使 Greeting 成为容器组件,我们可以将UI拆分为展示组件,我将在下面说明。

展示组件

展示(Presentational)组件 使用 props(属性) , render 和 context (无状态(stateless) API),并且可以是语法简单的函数式无状态组件。

const GreetingCard = (props) => {
  return (
    <div>
      <h1>Hello! {props.name}</h1>
    </div>
  )
}

展示组件仅从 props(属性) 接收数据和回调,这些 props(属性) 可以由其容器组件或父组件提供。

图注:蓝色是展示组件,灰色是容器组件

将容器组件的逻辑 和 展示组件的表现 封装在各自的组件中,结合在一起:

const GreetingCard = (props) => {
  return (
    <div>
      <h1>{props.name}</h1>
    </div>
  )
}

class Greeting extends React.Component {
  constructor() {
    super();
    this.state = {
      name: "",
    };
  }

  componentDidMount() {
    // AJAX
    this.setState(() => {
      return {
        name: "William",
      };
    });
  }

  render() {
    return (
      <div>
       <GreetingCard name={this.state.name} />
      </div>
    );
  }
}

如您所见,我已将 Greeting 类组件中的表示部分移除到其 函数式无状态的组件(即 GreetingCard 展示组件) 中。当然,这是一个非常简单的例子 – 对于更复杂的应用程序,这是至关重要的。

高阶组件(Higher order components , HOC )

高阶组件是一个将组件作为参数并返回新组件的函数。

这是一种功能强大的模式,用来为任意数量的组件提供数据,并可用于重用组件逻辑。 想想 react-router-v4 和 Redux 。 使用react-router-v4,您可以使用 withRouter() 继承作为 props 传递给组件的方法。 使用 Redux ,您可以访问在connect({})() 时作为 props 传递的操作。

图注:高阶组件用虚线表示,它是一种返回组件的函数

来看个示例:

import {withRouter} from 'react-router-dom';

class App extends React.Component {
  constructor() {
    super();
    this.state = {path: ''}
  }
  
  componentDidMount() {
    let pathName = this.props.location.pathname;
    this.setState(() => {
      return {
        path: pathName,
      }
    })
  }

  render() {
    return (
      <div>
        <h1>Hi! I'm being rendered at: {this.state.path}</h1>
      </div>
    )
  }
}

export default withRouter(App);

导出我的组件时,我用 react-router-v4 的 withRouter() 包裹它。在 App 的生命周期事件 componentDidMount() 中,我正在使用 this.props.location.pathname 提供的值更新 state(状态) 。现在,我的类组件 通过 withRouter() 包装后可以通过 props 访问 react-router-v4 的方法,因此 this.props.location.pathname 。这只是众多例子中的其中一个。

更多关于 高阶组件(Higher-Order Components) 说明请查看官方文档

渲染回调(Render callback)

愚人码头注:这种模式以前也叫 函数作为子组件(Function as Child Components) , 现在最新的叫法为:渲染属性(Render Props),请参阅官方文档

与高阶组件类似,渲染回调或渲染 props 用于共享或重用组件逻辑。
虽然许多开发人员更多地倾向于使用 HOC 的可重用逻辑,但使用渲染回调有一些非常好的理由和优势 – Michael Jackson的 “永不写另一个HOC ” 中得到了最好的解释。简而言之,渲染回调为我们难能可贵地减少了命名空间冲突,并更好的说明了逻辑来源。

图注:蓝色虚线表示渲染回调

class Counter extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0,
    };
  }

  increment = () => {
    this.setState(prevState => {
      return {
        count: prevState.count + 1,
      };
    });
  };

  render() {
    return (
      <div onClick={this.increment}>{this.props.children(this.state)}</div>
    );
  }
}

class App extends React.Component {
  render() {
    return (
      <Counter>
        {state => (
          <div>
            <h1>The count is: {state.count}</h1>
          </div>
        )}
      </Counter>
    );
  }
}

Counter 类中,我在 render 方法中嵌入 this.props.children 并将 this.state 作为参数。 在 App 类中,我能够将我的组件包装在 Counter 组件中,因此可以访问 Counter 的逻辑。 渲染回调部分是第28行,{state => ()} – bam! 我可以自动访问上面的 Counter 的 state(状态) 了。

更多关于 渲染属性(Render Props) 说明请查看官方文档

谢谢你的阅读!

我总是乐于接受任何建议,以便更好地解释 – 我通过写作来学习,所以这远远不够完美,这只是我对 React 组件模式的看法。

原文链接:https://medium.com/teamsubchannel/react-component-patterns-e7fb75be7bb0

关注亚洲城线上娱乐官方公众号

关注国内外最新最好的前端开发技术干货,获取最新前端开发资讯,致力于打造高质量的前端技术分享公众号

发表评论

电子邮件地址不会被公开。 必填项已用*标注