# State & 生命周期

* State 与 Props 类似，但是 state 是私有的，并且完全受控于当前组件
* 只要在相同的 DOM 节点中渲染，就仅有一个组件的 class 实例被创建使用，这就使得我们可以使用如 state 或生命周期方法等很多其他特性

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

1. 创建一个同名的 ES6 class，并继承于 React.Component
2. 添加一个空的 render() 方法
3. 将函数体移动到 render() 方法之中
4. 在 render() 方法中使用 this.props 替换 props
5. 删除剩余的空函数声明

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

\`\`\`jsx harmony

class Clock extends React.Component{ 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>
    )
}

ReactDOM.render(
    <Clock />,
    document.getElementById('root')
)
```

}

````
## 将生命周期方法添加到 Class 中

* 在具有许多组件的应用程序中，当组件被销毁时释放所占用的资源时非常重要的
* 当组件第一次被渲染到 DOM 中的时候，这在 React 中被称为 **挂载**
* 当 DOM 里面的组件被删除的时候，这在 React 中被称为 **卸载**
* 我们可以为 class 组件声明一些特殊的方法，当组件挂载和卸载时就回去执行这些方法，这些方法叫做 **生命周期方法**

* componentDidMount() 方法会在组件已经渲染到 DOM 中后运行
```jsx harmony

class Clcok extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            data: 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>
        )
    }
}

ReactDOM.render(
    <Clcok />,
    document.getElementById('root')
)
````

* 调用顺序
  1. 当 Clock 被传给 ReactDOM.render() 的时候，React 会调用 Clock 组件的构造函数。
  2. 之后 React 会调用组件的 render() 方法，这就是确定该在页面上展示什么的方式，然后 React 更新 DOM 来匹配 Clock 渲染的输出
  3. 当 CLock 的输出被插入 DOM 中后，React 就会调用 ComponentDidMount()&#x20;
  4. 浏览器每秒都会调用一次 tick() 方法，React 能够知道 state 已经改变了，然后会重新调用 render() 方法来确定页面上该显示什么
  5. 一旦 Clock 组件从 DOM 中移除，React 就会调用 ComponentWillUnmount() 生命周期方法

## 正确地使用 State

* 不要直接修改 State
  * this.setState() 是唯一可以给 this.state 赋值的地方
* State 的更新可能是异步的
  * 出于性能考虑，React 可能会把多个 setState() 调用合并成一个调用
  * 因为 this.props 和 this.state 可能会异步更新，所以你不要依赖他们的值来更新下一个状态
  * 解决这个问题，可以让 setState() 接收一个函数而不是一个对象，这个函数用上一个 state 作为一个参数，将此次更新被应用时的 props 做为第二个参数
  * \`\`\`jsx harmony // 箭头函数 this.setState((state, props) => ({ counter: state.counter + props.increment }))

    // 普通函数 this.setState(function (state, props) { return { counter: state.counter + props.increment } })

    \`\`\`
* State 的更新会被合并
  * 当你调用 setState() 的时候，React 会把你提供的对象合并到当前的 state&#x20;
  * 你的 state 包含几个独立的变量，然后你可以分别调用 setState() 来单独地更新它们

## 数据是向下流动的

* 不管是父组件或是子组件都无法知道某个组件是有状态的还是无状态的，并且它们也并不关心它是函数组件还是 class 组件
* 这也是为什么称 state 为局部的或是封装的原因，除了拥有并设置了它的组件，其他组件都是无法访问的
* 组件会在其 Props 中接收参数，但是组件本身无法知道它是来自于 父组件的 state 或是 props 还是手动输入的
* 这通常会被叫做 自上而下 或是 单向的数据库，任何的 state 总是所属于特定的组件，而且从该 state 派生的任何数据或 UI 只能影响树中低于它们的组件
