React 无状态组件(Stateless Component) 与高阶组件

无状态组件(Stateless Component) 是 React 0.14 之后推出的,大大增强了编写 React 组件的方便性,也提升了整体的渲染性能。

无状态组件 (Stateless Component)

function HelloComponent(props, /* context */) {
 return <div>Hello {props.name}</div>
}
ReactDOM.render(<HelloComponent name="Sebastian" />, mountNode)

HelloComponent 第一个参数是 props,第二个是 context。最后一句也可以这么写:

ReactDOM.render(HelloComponent{ name:"Sebastian" }, mountNode)

可以看到,原本需要写“类”定义(React.createClass 或者 class YourComponent extends React.Component)来创建自己组件的定义,现在被精简成了只写一个 render 函数。更值得一提的是,由于仅仅是一个无状态函数,React 在渲染的时候也省掉了将“组件类” 实例化的过程。

结合 ES6 的解构赋值,可以让代码更精简。例如下面这个 Input 组件:

function Input({ label, name, value, ...props }, { defaultTheme }) {
 const { theme, autoFocus, ...rootProps } = props
 return (
  <label
   htmlFor={name}
   children={label || defaultLabel}
   {...rootProps}
  >
  <input
   name={name}
   type="text"
   value={value || ''}
   theme={theme || defaultTheme}
   {...props}
  />
 )}
Input.contextTypes = {defaultTheme: React.PropTypes.object};

这个 Input 组件(仅仅是示例)直接实现了 label/inputText 的组合:

  1. defaultTheme 是从 Context 中解构出来的,如果 props 没有设定 theme,就将用 defaultTheme 替代。
  2. autoFocus 需要被传递到底层的 inputText 而不能同时遗留给 label,因此会先通过 { theme, autoFocus, ...rootProps } = props 拿出来。

无状态组件用来实现 Server 端渲染也很方便,只要避免去直接访问各种 DOM 方法。

无状态组件与组件的生命周期方法

我们可以看到,无状态组件就剩了一个 render 方法,因此也就没有没法实现组件的生命周期方法,例如 componentDidMount, componentWillUnmount 等。那么如果需要让我们的 Input 组件能够响应窗口大小的变化,那么该如何实现呢?这其实还是要引入“有状态的组件”,只不过这个“有状态的组件”可以不仅仅为 "Input" 组件服务。

const ExecutionEnvironment = require('react/lib/ExecutionEnvironment')
const defaultViewport = { width: 1366, height: 768 }; // Default size for server-side rendering

function withViewport(ComposedComponent) {
 return class Viewport extends React.Component {
  state = {
   // Server 端渲染和单元测试的时候可未必有 DOM 存在
   viewport: ExecutionEnvironment.canUseDOM ?
    { width: window.innerWidth, height: window.innerHeight } : defaultViewport
  }
  componentDidMount() {
   // Server 端渲染是不会执行到 `componentDidMount` 的,只会执行到 `componentWillMount`
   window.addEventListener('resize', this.handleWindowResize)
   window.addEventListener('orientationchange', this.handleWindowResize)
  }
  componentWillUnmount() {
   window.removeEventListener('resize', this.handleWindowResize)
   window.removeEventListener('orientationchange', this.handleWindowResize)
  }
  render() {
   return <ComposedComponent {...this.props} viewport={this.state.viewport}/>
  }

  handleWindowResize() {
   const { viewport } = this.state
   if (viewport.width !== window.innerWidth || viewport.height !== window.innerHeight) {
    this.setState({ viewport: { width: window.innerWidth, height: window.innerHeight } })
   }
  }
 }
}

*** 专业的实现参看 https://github.com/kriasoft/react-decorators ***

那么,下面我们就可以创建出一个有机会响应窗口大小变化的 Input 组件:

const SizeableInput = withViewport(Input)
ReactDOM.render(<SizeableInput name="username" label="Username" {...props} />, mountNode)

withViewort 作为一个 "高阶组件" 可不仅仅是为了 Input 服务的。它可以为你需要的任何组件添加上 viewport 属性,当窗口大小变化时,触发重绘。

如果你用过 Redux,那么应该也熟悉 "connect decorator" 的用法。"connect decorator" 也是一个高阶组件,因此,你可以继续来“拼凑”:

const UserNameInput = connect(
 state => ({ value: state.username })
)(SizeableInput)

高阶组件的存在有两个好处:

  • 当写着写着无状态组件的时候,有一天忽然发现需要状态处理了,那么无需彻底返工:)
  • 往往我们需要状态的时候,这个需求是可以重用的,例如上面的 withViewport,今后可以用来给其他组件(无论是否是无状态组件)添加 viewport 属性。

高阶组件加无状态组件,则大大增强了整个代码的可测试性和可维护性。同时不断“诱使”我们写出组合性更好的代码。

无状态组件不支持 "ref"

有一点遗憾的是无状态组件不支持 "ref"。原理很简单,因为在 React 调用到无状态组件的方法之前,是没有一个实例化的过程的,因此也就没有所谓的 "ref"。

ref 和 findDOMNode 这个组合,实际上是打破了父子组件之间仅仅通过 props 来传递状态的约定,是危险且肮脏,需要避免。

无状态组件尚不支持 babel-plugin-react-transform 的 Hot Module Replacement

如果你是用 Webpack 以及 HMR,用 babel-plugin-react-transform来做 jsx 转换等,那么当你在编辑器中修改无状态组件的源代码的时候,HMR 并不会在浏览器中自动载入修改后的代码。具体问题跟踪请参 https://github.com/gaearon/babel-plugin-react-transform/issues/57

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • 详解在React.js中使用PureComponent的重要性和使用方式

    一.介绍PureComponent React 15.3在2016.06.29发布了,这个版本最值得关注的是支持了 React.PureComponent ,它取代了之前的 pure-render-mixin .在本文中,我们将讨论 PureComponent 的重要性和使用场景. React.PureComponent最重要的一个用处就是优化React应用,这很容易快速地实现.使用 React.PureComponent 对性能的提升是非常可观的,因为它减少了应用中的渲染次数. PureCom

  • React之PureComponent的使用作用

    React避免重复渲染 React在渲染出的UI内部建立和维护了一个内层的实现方式,它包括了从组件返回的React元素.这种实现方式使得React避免了一些不必要的创建和关联DOM节点,因为这样做可能比直接操作JavaScript对象更慢一些,它被称之为"虚拟DOM". 当一个组件的props或者state改变时,React通过比较新返回的元素和之前渲染的元素来决定是否有必要更新实际的DOM.当他们不相等时,React会更新DOM. 在一些情况下,你的组件可以通过重写这个生命周期函数s

  • React styled-components设置组件属性的方法

    问题 最近在试着用react做一个音乐播放器,在这之前其实并不了解styled-components,但由于使用css in js并且想实现hover效果,百度各种解决方案后发现了styled-components这个好东西,如果你看到了这篇博客,就证明你应该了解或者熟练运用styled-components了. 回到项目开发中,一个音乐播放器应该由多个组件组成,其中有一个list组件用于展示歌曲列表,就像这样 鹅...好像暴露了我的喜好... 每一列就是一个div(当然ul也是可以的),为了方

  • React Component存在的几种形式详解

    前言 最近项目基本都是用 React,今天总结分享 React Component 常见的几种形式,如果你在写 React 时经常不知道怎么拆分代码,这篇文章或许对你有所帮助. React.Component是一个抽象基类.这意味着直接引用React.Component是毫无意义的.你可以实现一个它的子类,并且至少定义一个render()方法即可使用. 为了更充分理解 React,先搞懂平时写的 JSX 是什么.初学的时候有比较大困惑,这是一门新语言吗?大部分人是匆匆扫过文档就开始开发.通过 b

  • 简谈创建React Component的几种方式

    前言 当我们谈起React的时候,多半会将注意力集中在组件之上,思考如何将页面划分成一个个组件,以及如何编写可复用的组件.但对于接触React不久,还没有真正用它做一个完整项目的人来说,理解如何创建一个组件也并不那么简单. 在最开始的时候我以为创建组件只需要调用createClass这个api就可以了:但学习了ES6的语法后,又知道了可以利用继承,通过extends React.component来创建组件:后来在阅读别人代码的时候又发现了PureComponent以及完全没有继承,仅仅通过返回

  • React 无状态组件(Stateless Component) 与高阶组件

    无状态组件(Stateless Component) 是 React 0.14 之后推出的,大大增强了编写 React 组件的方便性,也提升了整体的渲染性能. 无状态组件 (Stateless Component) function HelloComponent(props, /* context */) { return <div>Hello {props.name}</div> } ReactDOM.render(<HelloComponent name="Se

  • React学习笔记之高阶组件应用

    是什么 高阶组件是一个函数,能够接受一个组件并返回一个新的组件.没有任何副作用. 为什么用 封装并抽离组件的通用逻辑,让此部分逻辑在组件间更好地被复用. 如何用 //hoc为我们的高阶组件,可以使用es7装饰器语法来使用高阶组件 //当然也可以不用es7,如:let hocHello = hoc(Hello),只是es7的语法更优雅一些. //高阶组件可以叠加使用,可以对一个组件使用多个高阶组件 @hoc class Hello extends React.Component { // } 如何

  • React 高阶组件HOC用法归纳

    一句话介绍HOC 何为高阶组件(HOC),根据官方文档的解释:"高阶组件是react中复用组件逻辑的一项高级技术.它不属于react API的组成部分,它是从react自身组合性质中抽离出来的一种模式.具体来说,高阶组件是函数,它接受一个组件作为参数,然后返回一个新的组件 使用场景 将几个功能相似的组件里面的方法和react特性(如生命周期里面的副作用)提取到HOC中,然后向HOC传入需要封装的组件.最后将公用的方法传给组件. 优势 使代码简洁优雅.代码量更少 HOC(高阶组件) /* HOC(

  • React高阶组件使用教程详解

    目录 高阶组件(HOC) 概述 使用HOC解决横切关注点问题 不用改变原始组件使用组合 约定-将不相关的 props 传递给被包裹的组件 约定-最大化可组合性 约定-包装显示名称以便轻松调试 使用高阶组件的注意事项 高阶组件(HOC) 概述 是React复用组件逻辑的一种高级技巧,是一种基于React组合特性而形成的设计模式 高阶组件是参数为组件,返回值为新组件的函数 简单理解: 高阶组件本身是 函数,传参数是组件,返回值也是组件: 高阶组件不用关心数据是如何渲染的,只用关心逻辑即可 被包装的组

  • 浅谈React高阶组件

    前段时间在工作中写Hybrid页面时遇到了这样的一个场景,公司需要一系列的活动组件,在每个组件注册的时候都需要调用App端提供的一个接口.一开始也考虑了几种方式,包括mixin.组件继承以及react高阶组件.但经过了种种衡量,最后选择使用了高阶组件的做法. 那什么是高级组件?首先你得先了解请求ES6中的class只是语法糖,本质还是原型继承.能够更好的进行说明,我们将不会修改组件的代码.而是通过提供一些能够包裹组件的组件, 并通过一些额外的功能来增强组件.这样的组件我们称之为高阶组件(High

  • react高阶组件添加和删除props

    唠叨几句啦 在看程墨老师的深入浅出高阶组件,开头一点提了一个需要,创建两个高阶组件,一个能给传入的元素自定义添加props,一个是删除特定的props.我刚刚做了一下,发现高阶组件需要区分好传入的是class还是react element, 同时也需要注意好return回去的是啥.顺便提一下高阶组件的概念,就说一个函数,能够接受一个组件作为参数,然后返回的时候,这个组件就带有这个高阶组件给的某些特性.我理解就跟掉泥坑了,得带点土出来一个道理. 对比一下两个组件,贴代码时刻来啦 删除属性的高阶组件

  • React高阶组件的使用浅析

    目录 高阶函数 高阶组件 react常见的高阶函数 高阶组件形式 在学习高阶组件前,首先我们了解一下高阶函数 高阶函数 把一个函数作为另一个函数的参数,那么这个函数就是高阶函数 高阶组件 一个组件的参数是组件,并且返回值是一个组件,我们称这类组件为高阶组件 react常见的高阶函数 withRouter() memo() react-redux中connect 高阶组件形式 React中的高阶组件主要有两种形式:属性代理和反向继承 属性代理:一个函数接收一个WrappedComponent组件作

  • React 高阶组件与Render Props优缺点详解

    目录 高阶组件 增强型高级组件 注入型高阶组件 高阶组件 VS Render Props 总结 高阶组件 高阶组件(HOC)是一个接受组件作为参数并返回一个新组件的函数,如果多个组件有相同的逻辑,将这些逻辑用函数封装,使它们能跨组件共用,这种用法称为高阶组件.下面的代码演示什么是高阶组件: export default function WithPrintLog(InnerComponent) { return class extends React.Component{ componentDi

  • React HOC高阶组件深入讲解

    目录 1. 概念 2. 属性代理 2.1 代理props 2.2 条件渲染 2.3 添加状态 3. 反向继承 3.1 拦截渲染 3.2 劫持生命周期 3.3 操作state 3.4 修改react树 3.5 记录渲染性能 4. 使用装饰器 4.1 安装和配置 4.2 使用 5.总结 1. 概念 高阶组件和高阶函数的类似,使用函数接收一个组件,并返回一个组件. function withList(WrapComponent) { return class extends Component { r

  • react高阶组件经典应用之权限控制详解

    前言 所谓高级组件,即:接受一个组件作为参数,并且其返回值也为一个react组件 而大家应该都知道,权限控制算是软件项目中的常用功能了.在网站中,权限控制一般分为两个维度:页面级别和页面元素级别. 我们来说说页面元素粒度的权限控制.在某个页面中,有个"创建用户"的按钮,管理员才能看到. 一般想到的做法类似这样 class Page extends Component{ render() { let hasCreatePermission = tool.getAuth("cre

随机推荐