ps:本文大部分资料、内容来自react官方文档
在元素渲染中写的计时器,通过每秒执行一次ReactDOM.render()
来更新页面,但现在希望只写一次代码,只调用一次ReactDOM.render()
就能让
组件自动更新,这需要在
组件中添加state
.
React.Component
。render()
方法。render()
方法之中。render()
方法中使用 this.props
组件class Clock extends React.Component {
render() {
return (
<h2>当前时间:{this.props.date.toLocaleTimeString()}.</h2>
);
}
}
render()
方法中的 this.props.date
替换成 this.state.date
this.state
赋初值
元素中的 date
属性class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
render() {
return (
<h2>当前时间:{this.state.date.toLocaleTimeString()}.</h2>
);
}
}
当Clocl
组件第一次被渲染的时候,为其设置一个计时器。这在react中被称为“挂载(mount)
”,同样,当Clock
组件在DOM中被删除时,应取消这个计时器。这在react中被称为“卸载(unmount)
”
可以在class组件中声明生命周期方法
,当组件被挂载或卸载时就会自动执行这些方法
class Clock extends React.Component {
...
componentDidMount(){
...
}
componentWillUnmount(){
...
}
...
}
componentDidMount()方法:
componentDidMount(){
this.timerID = setInterval
}
componentWillUnmount()方法:
componentWillUnmount() {
clearInterval(this.timerID);
}
在Clock
组件中添加一个timer()
方法,每秒调用一次,使用this.setState()
方法来每秒更新组件的state
最后代码:
class Clock extends React.Component {
constructor(props) {
super(props);
//为一开始显示的时间提供state.date
this.state = {date: new Date()};
}
componentDidMount() {
//每秒从timer()获取一次当前时间
this.timerID = setInterval(() => {
this.timer();
}, 1000);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
timer() {
this.setState({
//return中的h2需要的this.state.date从此处获取
date: new Date()
});
}
render() {
return (
//获取state中的date属性的值
<h2>当前时间:{this.state.date.toLocaleTimeString()}.</h2>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('react_test')
);
执行顺序:
被传给 ReactDOM.render()
的时候,React 会调用 Clock
组件的构造函数。因为 Clock
需要显示当前的时间,所以它会用一个包含当前时间的对象来初始化 this.state
。我们会在之后更新 state。render()
方法。这就是 React 确定该在页面上展示什么的方式。然后 React 更新 DOM 来匹配 Clock
渲染的输出。Clock
的输出被插入到 DOM 中后,React 就会调用 ComponentDidMount()
生命周期方法。在这个方法中,Clock
组件向浏览器请求设置一个计时器来每秒调用一次组件的 tick()
方法。tick()
方法。 在这方法之中,Clock
组件会通过调用 setState()
来计划进行一次 UI 更新。得益于 setState()
的调用,React 能够知道 state 已经改变了,然后会重新调用 render()
方法来确定页面上该显示什么。这一次,render()
方法中的 this.state.date
就不一样了,如此以来就会渲染输出更新过的时间。React 也会相应的更新 DOM。Clock
组件从 DOM 中被移除,React 就会调用 componentWillUnmount()
生命周期方法,这样计时器就停止了。//错误的设置state姿势:
//此方式不会渲染到组件上
this.state.date = new Date();
//正确的state复制姿势:
//构造函数时唯一可用给this.state赋值的地方
this.state({ date: new Date() });
出于性能考虑,React 可能会把多个 setState()
调用合并成一个调用。
因为 this.props
和 this.state
可能会异步更新,所以你不要依赖他们的值来更新下一个状态。
例如,此代码可能会无法更新计数器:
// Wrong
this.setState({
counter: this.state.counter + this.props.increment,
});
要解决这个问题,可以让 setState()
接收一个函数而不是一个对象。这个函数用上一个 state 作为第一个参数,将此次更新被应用时的 props 做为第二个参数:
// Correct
this.setState((state, props) => ({
counter: state.counter + props.increment
}));
上面使用了箭头函数,不过使用普通的函数也同样可以:
// Correct
this.setState(function(state, props) {
return {
counter: state.counter + props.increment
};
});
当你调用 setState()
的时候,React 会把你提供的对象合并到当前的 state。
例如,你的 state 包含几个独立的变量:
constructor(props) {
super(props);
this.state = {
posts: [],
comments: []
};
}
然后你可以分别调用 setState()
来单独地更新它们:
componentDidMount() {
fetchPosts().then(response => {
this.setState({
posts: response.posts
});
});
fetchComments().then(response => {
this.setState({
comments: response.comments
});
});
}
这里的合并是浅合并,所以 this.setState({comments})
完整保留了 this.state.posts
, 但是完全替换了 this.state.comments
。
不管是父组件或是子组件都无法知道某个组件是有状态的还是无状态的,并且它们也并不关心它是函数组件还是 class 组件。
这就是为什么称 state 为局部的或是封装的的原因。除了拥有并设置了它的组件,其他组件都无法访问。
组件可以选择把它的 state 作为 props 向下传递到它的子组件中:
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
这对于自定义组件同样适用:
<FormattedDate date={this.state.date} />
FormattedDate
组件会在其 props 中接收参数 date
,但是组件本身无法知道它是来自于 Clock
的 state,或是 Clock
的 props,还是手动输入的:
function FormattedDate(props) {
return <h2>It is {props.date.toLocaleTimeString()}.</h2>;
}
这通常会被叫做“自上而下”或是“单向”的数据流。任何的 state 总是所属于特定的组件,而且从该 state 派生的任何数据或 UI 只能影响树中“低于”它们的组件。
如果你把一个以组件构成的树想象成一个 props 的数据瀑布的话,那么每一个组件的 state 就像是在任意一点上给瀑布增加额外的水源,但是它只能向下流动。
为了证明每个组件都是真正独立的,我们可以创建一个渲染三个 Clock
的 App
组件:
function App() {
return (
<div>
<Clock />
<Clock />
<Clock />
</div>
);
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
每个 Clock
组件都会单独设置它自己的计时器并且更新它。
在 React 应用中,组件是有状态组件还是无状态组件属于组件实现的细节,它可能会随着时间的推移而改变。你可以在有状态的组件中使用无状态的组件,反之亦然。