1 如何封装真正可复用的 Clock 组件

1.1 封装时钟的外观

function Clock(props) {
  return (
    <div>
      <h1>Hello, world!</h1>
      <h2>It is { props.date.toLocaleTimeString() }.</h2>
    </div>
  );
}

function tick() {
  ReactDOM.render(
    <Clock date={ new Date() } />,
    document.getElementById('root')
  );
}

setInterval(tick, 1000);
  • Clock 组件需要设置一个计时器,并且需要每秒更新 UI

实现 Clock 组件自我更新

ReactDOM.render(
    <Clock />,
    document.getElementById('root')
  );
  • 需要在 Clock 组件中添加 state 来实现组件的自我更新

  • Stateprops 类似,但是 state 是私有的,并且完全受控于当前组件

1.2 将函数组件转换成 class 组件

class Clock extends React.Component {
  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is { this.props.date.toLocaleTimeString() }.</h2>
      </div>
    );
  }
}
  • 每次组件更新时 render 方法都会被调用,但只要在相同的 DOM 节点中渲染,就仅有一个 Clock 组件的 class 实例被创建使用

1.3 向 class 组件中添加局部的 state

  • 通过以下三步将 dateprops 移动到 state
1. 把 render() 方法中的 this.props.date 替换成 this.state.date
class Clock extends React.Component {
  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is { this.state.date.toLocaleTimeString() }.</h2>
      </div>
    );
  }
}
2. 添加一个 class 构造函数,然后在该函数中为 this.state 赋初值
class Clock extends React.Component {
  // 将props传递到父类的构造函数中
  constructor(props) {
    super(props)
    this.state = { date: new Date() }
  }
  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is { this.state.date.toLocaleTimeString() }.</h2>
      </div>
    );
  }
}
3. 移除 <Clock /> 元素中的 date 属性
ReactDOM.render(
  <Clock />,
  document.getElementById('root')
)

1.4 将生命周期方法添加到 class 中

1. 在组件被挂载到 DOM 后运行
componentDidMount() {
  this.timerID = setInterval(
    () => this.tick(),
    1000
  )
}
2. 在组件卸载后清除计时器
componentWillUnmount() {
  clearInterval(this.timerID)
}
3. 使用 this.setState() 来时刻更新组件 state
class Clock extends React.Component {
  constructor(props) {
    super(props)
    this.state = { date: new Date() }
  }
  componentDidMount() {
    this.timerID = setInterval(
      () => this.tick(),
      1000
    )
  }
  componentWillUnmount() {
    clearInterval(this.timerID)
  }
  tick() {
    this.setState({
      date: new Date()
    })
  }
  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is { this.state.date.toLocaleTimeString() }.</h2>
      </div>
    );
  }
}
RenderDOM.render(
  <Clock />,
  document.getElementById('root')
)

2 正确使用 State

2.1 不要直接修改 state

// wrong
this.state.commit = 'Hello'
// Correct
this.setState({
  commit: 'Hello'
})
  • 构造函数是唯一可以给 this.state 赋值的地方

2.2 State 的更新可能是异步的

  • 出于性能考虑,react 可能会把多个 setState() 调用合并成一个调用

  • 要解决异步调用的问题,可以让 setState() 接收一个函数而不是一个对象,这个函数用上一个 state 作为第一个参数,将此次更新被应用时的 props 作为第二个参数

// wrong
this.setState({
  counter: this.state.counter + this.props.increment
})
// Correct
this.setState((state, props) => {
  counter: state.counter + props.increment
})

2.3 State 的更新会被合并

2.4 数据是向下流动的

  • 任何的 state 总是所属于特定的组件,而且从该 state 派生的任何数据或 UI 只能影响树中“低于”它们的组件