state
state 是私有的,并且完全受控于当前组件。class组件才有state。
将函数组件转化成class组件
function Clock(props) {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {props.date.toLocaleTimeString()}.</h2>
</div>
);
}
#转化为class组件
class Clock extends React.Component {
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.props.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
- 创建一个与函数同名的类并且继承于
React.Component。 - 添加一个空的
render()方法, 将函数体移动到render()方法之中。 - 在
render()方法中使用this.props替换props。
每次组件更新时 render 方法都会被调用,但只要在相同的 DOM 节点中渲染 <Clock /> ,就仅有一个 Clock 组件的 class 实例被创建使用。这就使得我们可以使用如 state 或生命周期方法等很多其他特性。
在class组件中添加局部state
class Clock extends React.Component {
constructor(props) {
#Class 组件应该始终使用 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 中
在具有许多组件的应用程序中,当组件被销毁时释放所占用的资源是非常重要的。
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
componentDidMount() {
#当 `Clock` 组件第一次被渲染到 DOM 中的时候,就为其设置一个计时器。
#这在 React 中被称为“挂载(mount)”。
this.timerID = setInterval(
() => this.tick(),
1000
);
}
componentWillUnmount() {
#当 DOM 中 `Clock` 组件被删除的时候,应该清除计时器。
#这在 React 中被称为“卸载(unmount)”。
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>
);
}
}
执行顺序:
- 当 <Clock /> 被传给 ReactDOM.render()的时候,React 会调用 Clock 组件的构造函数。因为 Clock 需要显示当前的时间,所以它会用一个包含当前时间的对象来初始化 this.state。我们会在之后更新 state。
- 之后 React 会调用组件的 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的使用
- 不要直接修改 State
this.state.comment = 'Hello'; // Wrong
#应该使用 setState():
this.setState({comment: 'Hello'}); // Correct
- State 的更新可能是异步的
出于性能考虑,React 可能会把多个 setState() 调用合并成一个调用。因为 this.props 和 this.state 可能会异步更新,所以你不要依赖他们的值来更新下一个状态。
this.setState({
counter: this.state.counter + this.props.increment,
}); // Wrong
#要解决这个问题,可以让 setState() 接收一个函数而不是一个对象。这个函数用上一个 state 作为第一个参数,将此次更新被应用时的 props 做为第二个参数:
this.setState((state, props) => ({
counter: state.counter + props.increment
})); // Correct
- State 的更新会被合并
调用 setState() 的时候,React 会把你提供的对象合并到当前的 state。这里的合并是浅合并。
constructor(props) {
super(props);
this.state = {
posts: [],
comments: []
};
}
componentDidMount() {
# 可以分别调用 setState() 来单独地更新它们
fetchPosts().then(response => {
this.setState({
posts: response.posts
});
});
fetchComments().then(response => {
this.setState({
comments: response.comments
});
});
}
数据是自顶向下流动的
父组件或子组件都不能知道某个组件是有状态还是无状态,并且它们不应该关心某组件是被定义为一个函数还是一个class。
这就是为什么state通常被称为局部或封装。 除了拥有并设置它的组件外,其它组件不可访问。
组件可以选择把它的 state 作为 props 向下传递到它的子组件中。但子组件本身无法知道它是来自于父组件的 state、props、还是手动输入的。
这通常被称为“自顶向下”或“单向”数据流。 任何状态始终由某些特定组件所有,并且从该状态导出的任何数据或 UI 只能影响树中下方的组件。
props
state 和 props 主要的区别在于 props 是不可变的,而 state 可以根据与用户交互来改变。
- props 验证
props 验证使用 propTypes,它可以保证我们的应用组件被正确使用,React.PropTypes 提供很多验证器 (validator) 来验证传入数据是否有效。当向 props 传入无效数据时,JavaScript 控制台会抛出警告。
let title = "测试";
// let title = 123;
class TestTitle extends React.Component {
render() {
return (
<h1>Hello, {this.props.title}</h1>
);
}
}
#当title不是string时(123),抛出警告。
TestTitle.propTypes = {
title: PropTypes.string
};
ReactDOM.render(
<TestTitle title={title} />,
document.getElementById('root')
);









网友评论