React从0到1系列第三章——深刻理解React组件的组合、props和state

1、组件的组和、组件树

想要理解数据是如何在组件树内自上往下流动(Data Flow),须要先明白React组件的组合、嵌套和组件树的构成。html

注意:自定义的组件都必需要用大写字母开头,普通的HTML标签都用小写字母开头。react

下面是用React.js组建的房子为例子来讲明一下组件的组和应用。一个房子里面有一个房间和一个厕所。房间里有一台电脑和两条狗,以下:git

// 电脑
class Computer extends Component {  render () {  return <h3>this is Computer</h3>  } }  // 狗 class Dog extends Component {  render () {  return <h3>this is Dog</h3>  } }  // 房间 class Room extends Component {  render () {  return (  <div>  <h2>this is Room</h2>  <Computer />  <Dog />  <Dog />  </div>  )  } }  // 厕所 class WC extends Component {  render () {  return <h2>this is WC</h2>  } }  // 房子 class House extends Component {  render () {  return <div>  <h1>this is House</h1>  <Room />  <WC />  </div>  } } 复制代码

最后页面会输出以下图:github

组件能够和组件组合在一块儿,组件内部可使用别的组件。这样的组合嵌套,最后构成一个所谓的组件树。用下图表示这种树状结构它们之间的关系:web

2、组件的state

state是组件的当前状态,React根据状态state呈现不一样的UI展现。React.jsstate 就是用来存储这种可变化的状态的。一旦状态(数据)更改,组件就会自动调用render从新渲染 UI,这个更改的动做经过this.setState()方法来触发。数组

下面是一个简单的修改状态的小例子:异步

class Like extends Component {
 constructor(props) {  super(props)  this.state = { isLike: false }  }  handleClick () {  this.setState({  isLike: !this.state.isLike  })  }  render () {  return (  <h3 onClick={this.handleClick.bind(this)}>  你{this.state.isLike ? '喜欢' : '不喜欢'}她  </h3>  )  } } 复制代码

isLike存放在实例的state 对象当中,这个对象在构造函数里面初始化。在这个组件的render函数内,会根据组件的state 的中的isLike的不一样显示喜欢或不喜欢。setState()方法由父类Component所提供。编辑器

正确的使用statesetState()方法

setState()方法,它接受一个对象或者函数做为参数。函数

  • 对象做为参数时
handleClick () {
 // 只须要传入须要更新的部分,而不须要入整个对象。  this.setState({  isLike: !this.state.isLike  }) } 复制代码
  • 函数做为参数时
handleClick () {
 this.setState(() => {  return { isLike: !this.state.isLike }  }) } 复制代码

不能直接修改state

不能直接修改state,若是直接修改state,组件并不会从新触发render方法。性能

// 错误
this.state.isLike = !this.state.isLike  // 正确 this.setState({  isLike: !this.state.isLike }) 复制代码

state的更新是异步的

在调用setState()的时候React.js并不会立刻修改statesetState()只是把要修改的状态放入一个更新队列中。React会优化真正的执行时机。以下:

class AddCount extends Component {
 constructor(props) {  super(props)  this.state = { count: 0 }  }  handleClick () {  this.setState({ count: this.state.count + 1 })  // 0  console.log(this.state.count)  }  render () {  return (  <div>  {/* 1 */}  <h2>{this.state.count}</h2>  <button onClick={this.handleClick.bind(this)}>添加</button>  </div>  )  } }  复制代码

当点击添加按钮的时候页面输出的是1,打印出来的倒是0。因此在调用setState()以后,this.state不会当即映射为新的值。

解决方案是setState(updater, [callback])接收更新后能够传入一个回调函数,一旦setState()完成而且组件重绘以后,这个回调函数将会被调用。

handleClick () {
 this.setState({ count: this.state.count + 1 }, () => {  // 1  console.log(this.state.count)  }) } 复制代码

setState()浅合并

React.js出于性能缘由,可能会将屡次setState()的状态修改合并成一次状态修改。因此不要依赖当前的setState()计算下个State。以下的一个计数器:

class AddCount extends Component {
 constructor(props) {  super(props)  this.state = { count: 0 }  }  handleClick () {  this.setState({ count: this.state.count + 1 })  this.setState({ count: this.state.count + 1 })  }  render () {  return (  <div>  <h2>{this.state.count}</h2>  <button onClick={this.handleClick.bind(this)}>添加</button>  </div>  )  } } 复制代码

当点击添加按钮的时候,会发现页面输出的是1。虽然咱们setState()方法调用了两次。是由于当咱们调用setState()修改组件状态时,组件state的更新实际上是一个浅合并的过程,至关于:

Object.assign(
 previousState,  {count: state.count + 1},  {count: state.count + 1},  ... )  复制代码

因此若是后续操做要依赖前一个setState()的结果的状况下就要使用函数来做为setState()参数。React.js会把上一个setState()的结果传入这个函数,你就可使用上一个正确的结果进行操做,而后返回一个对象来更新state。

handleClick () {
 this.setState({ count: this.state.count + 1 })  this.setState((prevState) => {  // 1  console.log(prevState.count)  return { count: prevState.count + 1 }  }) }} 复制代码

把上次操做setState()更新的值传入到下一个setState()里,就能够正确的显示count了。

stateImmutable(不可变性)

React官方建议把state看成是的Immutable(不可变性)对象,state中包含的全部状态都应该是不可变对象。当state中的某个状态发生变化,咱们应该从新建立这个状态对象,而不是直接修改原来的状态。

关于Immutable能够参考Immutable详解及React中实践

  1. 若有一个数组类型的状态 names,当向 name中添加一个名字时,使用数组的 concat方法或 ES6的扩展运算符:
// 1
this.setState(prevState => ({  names: prevState.names.concat(['lisi']) }))  // 2 this.setState(prevState => ({  names: [...prevState.names,'lisi'] }))  复制代码

注意不要使用pushpopshiftunshiftsplice等方法修改数组类型的状态,由于这些方法都是在原数组的基础上修改,而concatslice等返回一个新的数组。

  1. 若有一个对象类型的状态 person,为了避免改变本来的对象,咱们可使用 Object.assign 方法或者对象扩展属性:
// 1
function updatePerson (person) {  return Object.assign({}, person, { age: 20 }) }  // 2 function updatePerson (person) {  return {...person,age:20} }  复制代码

建立新的状态对象要避免使用会直接修改原对象的方法,而是使用能够返回一个新对象的方法。

当处理深层嵌套对象时,以immutable(不可变)的方式更新会很麻烦。可使用一些Immutable的JS库,如Immutable.js来简化开发。

3、组件的props

组件是相互独立、可复用的单元,一个组件可能在不一样地方被用到。可是在不一样的场景下对这个组件的需求可能会根据状况有所不一样,因此要针对相同的组件传入不一样的配置项。

React.jsprops就能够帮助咱们达到这个效果。每一个组件均可以接受一个props参数,它是一个对象,包含了全部你对这个组件的配置。

下面是一个能够经过传入props来控制是否是能够点击修改喜欢或不喜欢的文字:

class Like extends Component {
 constructor(props) {  super(props)  this.state = { isLike: false }  }  handleClick () {  this.setState({  isLike: !this.state.isLike  })  }  render () {  const clickable = this.props.clickable  return (  <h3 onClick={clickable ? this.handleClick.bind(this) : () => { }} >  你{ this.state.isLike ? '喜欢' : '不喜欢'}她  </h3>  )  } }  class App extends Component {   render () {  return (  <div>  <Like clickable={true} />  <Like clickable={false} />  </div>  )  } } 复制代码

组件内部是经过this.props的方式获取到组件的参数的,若是this.props里面有须要的属性咱们就采用相应的属性,没有的话就用默认的属性。在使用一个组件的时候,能够把参数放在标签的属性当中,全部的属性都会做为props`对象的键值。

默认配置defaultProps

React.js也提供了一种方式defaultProps,能够方便的作到默认配置。

class Like extends Component {
 static defaultProps = {  clickable: true  }  //... } 复制代码

props 不可变

props一旦传入进来就不能够在组件内部对它进行修改。可是能够经过父组件主动从新渲染的方式来传入新的props,从而达到更新的效果。以下:

class App extends Component {
  constructor(props) {  super(props)  this.state = { clickable: false }  }   handleClickOnChange () {  this.setState({  clickable: !this.state.clickable  })  }  render () {  return (  <div>  <Like clickable={this.state.clickable} />  <Like clickable={this.state.clickable} />  <button onClick={this.handleClickOnChange.bind(this)}>  修改clickable  </button>  </div>  )  }  复制代码

参考

https://zh-hans.reactjs.org/

https://juejin.im/entry/59522bdb6fb9a06b9a516113

https://github.com/camsong/blog/issues/3

https://github.com/lcxfs1991/blog/issues/8

http://huziketang.mangojuice.top/books/react/

https://dev.to/rleija_/master-the-art-of-react-state-and-props-in-5-minutes-3hao

http://product.dangdang.com/25249546.html

本文使用 mdnice 排版

相关文章
相关标签/搜索