React State(状态)
React 把组件看成是一个状态机(State Machines)。通过与用户的交互,实现不同状态,然后渲染 UI,让用户界面和数据保持一致。
React 里,只需更新组件的 state,然后根据新的 state 重新渲染用户界面(不需要操作 DOM)。
Demo.js :
import React from 'react'
class Demo extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      text: 'Hello,world!',
      style: {
        color: 'pink'
      },
      date: new Date()
    }
  }
  render () {
    return (
      <div>
        <p style={this.state.style}>{this.state.text}</p>
        <p>{this.state.date.toLocaleTimeString()}</p>
      </div>
    )
  }
}
export default Demo
上面实例,创建了一个名称扩展为 React.Component 的 ES6 类,在 render() 方法中使用 this.state 来修改当前的时间。
添加一个类构造函数来初始化状态 this.state,类组件应始终使用 props 调用基础构造函数。
页面效果:
 
将生命周期方法添加到类中
Demo.js :
import React from 'react'
class Timer extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      date: new Date()
    }
  }
  componentDidMount () {
    // 每当 `Timer` 组件第一次加载到 `DOM` 中的时候,生成定时器,这在 `React` 中被称为挂载。
    this.timerID = setInterval(() => {
      this.tick()
    }, 1000);
  }
  tick () {
    this.setState({
      date: new Date()
    })
  }
  componentWillUnmount () {
    // 每当 `Timer` 生成的这个 `DOM` 被移除的时候,清除定时器,这在 `React` 中被称为卸载
    clearInterval(this.timerID)
  }
  render () {
    return (
      <p>当前时间为: {this.state.date.toLocaleTimeString()}</p>
    )
  }
}
export default Timer
App.js 中引入 Demo.js:
import './assets/css/App.css';
import Demo from './components/Demo'
function App () {
  return (
    <div className="App">
      <Demo />
    </div>
  );
}
export default App;
上面实例,在组件输出到 DOM 后,会执行 componentDidMount() 钩子,在这个钩子上设置一个定时器。
this.timerID 为定时器的 ID,组件卸载时,在 componentWillUnmount() 钩子中卸载定时器。
代码执行顺序为:
-  当 <Timer/>被传递给ReactDOM.render()时,React调用Timer组件的构造函数。 由于Timer需要显示当前时间,所以使用包含当前时间的对象来初始化this.state。 稍后会更新此状态。
-  React然后调用Timer组件的render()方法。这时,React了解屏幕上应该显示什么内容,然后React更新DOM以匹配Timer的渲染输出。
-  当 Timer的输出插入到DOM中时,React调用componentDidMount()生命周期钩子。 在其中,Timer组件要求浏览器设置一个定时器,每秒钟调用一次tick()函数。
-  浏览器每秒钟调用 tick()方法。Timer组件通过使用setState()来调度UI更新。 通过调用setState(),React知道状态已经改变,并再次调用render()方法来确定屏幕上应当显示什么。 这一次,render()方法中的this.state.date将不同,所以渲染输出将包含更新的时间,并相应地更新DOM。
-  一旦 Timer组件被从DOM中移除,React会调用componentWillUnmount()这个钩子函数,定时器也就会被清除。
数据自顶向下流动
父组件或子组件都不能知道某个组件是有状态还是无状态,并且它们不应该关心某组件是被定义为一个函数还是一个类。
状态通常被称为局部或封装,除了拥有并设置它的组件外,其它组件不可访问。
Demo.js :
import React from 'react'
function FormatterDate (props) {
  return <h1>当前时间为: {props.date.toLocaleTimeString()}</h1>
}
class Timer extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      date: new Date()
    }
  }
  componentDidMount () {
    // 每当 `Timer` 组件第一次加载到 `DOM` 中的时候,生成定时器,这在 `React` 中被称为挂载。
    this.timerID = setInterval(() => {
      this.tick()
    }, 1000);
  }
  tick () {
    this.setState({
      date: new Date()
    })
  }
  componentWillUnmount () {
    // 每当 `Timer` 生成的这个 `DOM` 被移除的时候,清除定时器,这在 `React` 中被称为卸载
    clearInterval(this.timerID)
  }
  render () {
    return (
      <FormatterDate date={this.state.date} />
    )
  }
}
export default Timer
上面实例, FormatterDate 组件将在其属性中接收到 date 值,并且不知道它是来自 Timer 状态、还是来自 Timer 的属性、亦或手工输入。
这通常被称为自顶向下或单向数据流。 任何状态始终由某些特定组件所有,并且从该状态导出的任何数据或 UI 只能影响树中下方的组件。
修改 App.js,渲染多个 Timer:
import './assets/css/App.css';
import Timer from './components/Demo'
function App () {
  return (
    <div className="App">
      <Timer />
      <Timer />
      <Timer />
      <Timer />
    </div>
  );
}
export default App;
页面效果:
 
上面实例,可以看到,每个 <Timer /> 组件都建立了自己的定时器并且独立更新,所有组件都是真正隔离的。



















