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 />
组件都建立了自己的定时器并且独立更新,所有组件都是真正隔离的。