更优雅的编写 React 组件 —— stateless functions

@eyasliu 2016-05-29 00:37:31发表于 eyasliu/blog react前端

纯函数

这篇文章 简单的介绍了一下纯函数

现在再来简单的概括一下:

纯函数就是相同的输入,永远都会有相同的输出,没有任何可观察的副作用

一个函数,如果与外界完全没有任何联系,那么内部的逻辑完全不受外界影响,所以永远会有相同的输出,如:

let addOne = x => x+1

他的输出永远都是对传入的值 +1 , 如果跟外界有联系,如

let shouldAdd = false;
let addOne = x => shouldAdd ? x+1 : x;

这样函数的输出,将会取决于外界环境,相同的输入,并不一定会有相同的输出,这样子函数就不纯了。

react state

组件的状态,是决定组件在不同时刻拥有的不同表现方式

有状态组件

编写组件的时候,组件可以有状态(state),一个组件在不同状态可以有不同输出。如果把一个组件看成是一个函数的话,那么以相同的输入(props),将会根据内部状态(state)的不同会得到不同的输出。那么这就不是纯函数了

// ...other code

render(){
    return this.state.open ? <div>open</div> : <span>close</span>
}

如上例子,state是组件内部决定的,外界无法控制,所以会根据state产生不同的输出

无状态组件

如果一个组件没有状态(state),那么组件的输出方式,将完全取决于两个参数:propscontext,只要有相同的 props 和 context ,那么他们的输出绝对是相同的。将组件比喻成函数的话,相同的输入(props 和 context) 永远都会有相同的输出

// ...other code

render(){
    return this.props.open ? <div>open</div> : <span>close</span>
}

如上例子,props是我们的输入,只要输入相同,那么他的输出也一定相同。

函数式无状态组件

我们编写组件的时候,并不是所有的组件都是需要状态的。由于react是以数据驱动,数据决定了react组件的输出结果,所以为了让我们组件更好控制,应该尽量的少使用状态。

编写函数式无状态组件

在 react 0.14 版本之后,提供了一种新的组件编写方式,就是 stateless functions,使用纯函数创建组件。

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

对比

我们用传统的方式和函数方式去创建组件,对比下语法

// 传统语法 —— es5
var Hello = React.createClass({
  getDefaultProps: function(){
    return {
      name: 'world'
    }
  },
  render: function(){
    return (
      <div>Hello {name}</div>
    )
  }
})

// 传统语法 —— es6
class Hello extends React.Component{
  constructor(props){
    super()
  }

  static defaultProps = {
    name: 'world'
  }

  render(){
    return (
      <div>Hello {name}</div>
    )
  }
}

// stateless functions
let Hello = ({name}) => <div>Hello {name}</div>
Hello.defaultProps = {
  name: 'world'
}

对比下有没有发现语法变得非常简单了呢

说明

一个无状态函数组件的形式:

let Hello = (props, context) => {
  return <div>Hello {props.name}</div>
}

Hello.defaultProps = {}
Hello.contextTypes = {}

// 使用时,就是使用平常组件时候去使用
<Hello name="Eyas" />

我们来精简这个函数,平时我们可以不用context,甚至连defaultProps都不需要:

let Hello = props => <div>Hello {props.name}</div>

这已经够精简了,我们还可以利用es6的解构赋值再来精简,适用于props的数量少的时候

let Hello = ({name}) => <div>Hello {name}</div>

很清爽的写法,这就完成了一个 react 组件的编写

优点

相比于 class 创建组件

  • 语法更简洁
  • 占内存更小(class 有 props context _context 等诸多属性),首次 render 的性能更好
  • 可以写成无副作用的纯函数
  • 可拓展性更强(函数的 compose,currying 等组合方式,比 class 的 extend/inherit 更灵活)

缺点

无生命周期函数

一个组件就是一个函数,函数应该是谈不上生命周期的,但是组件却是有生命周期,stateless functions 没有生命周期。当然了,我们其实可以使用 高阶组件 去实现生命周期

没有 this

在 stateless functions 中,this 是 undefined,所以是不能使用 this 变量。不过换个角度思考,this 是在运行时随时可以被修改或重新赋值,跟外界环境有着密切的联系,正是不使用this才会让组件变得更纯。