state
state是指组件的当前状态。组件根据状态state呈现不同的UI展示。
一旦状态(数据)更改,组件就会自动调用render重新渲染UI,通过this.setState方法来触发。
状态组件:当更改这个状态(数据)需要更新组件UI的就需要state。
无状态组件:这种组件没有状态,没有生命周期,只是简单的接受props渲染生成DOM结构。
无状态组件非常简单,开销很低,如果可能的话尽量使用无状态组件。比如使用箭头函数定义:
const HelloMessage = (props) => <div> Hello {props.name}</div>;
render(<HelloMessage name="John" />, mountNode);
使用类就允许我们使用其他特性,例如局部状态、生命周期钩子。
//定义一个类
class Clock extends React.Component {
// 添加一个类构造函数来初始化状态this.state
constructor(props) {
super(props);
this.state = {date: new Date()};
}// 当组件输出到DOM后会执行钩子
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(
<Clock />,
document.getElementById('root')
};
挂载(componentDidMount):
每当Clock组件第一次加载到DOM中的时候,需要生成这个定时器。
卸载(componentWillUnmount):
每当Clock生成的这个DOM被移除的时候,需要清除这个定时器。
实例讲解
1.当<Clock />被传递给ReactDOM.render()时,React调用Clock组件的构造函数。由于Clock需要显示当前时间,所以使用包含当前时间的对象来初始化this.state。我们稍后会更新此状态。
2.React然后调用Clock组件的render()方法。这是React了解屏幕上应该显示什么内容,然后React更新DOM以匹配Clock的渲染输出。
3.当Clock的输出插入到DOM中时,React调用componentDidMount()生命周期钩子。在其中,Clock组件要求浏览器设置一个定时器,每秒钟调用一次tick()。
4.浏览器每秒钟调用tick()方法。在其中,Clock组件通过使用包含当前时间的对象调用setState()来调度UI更新。通过调用setState(),React知道状态已经改变,并再次调用render()方法来确认屏幕上应当显示什么。这一次,render()方法中的this.state.date将不同,所以渲染输出将包含更新的时间,并相应地更新DOM。
5.一旦Clock组件被从DOM中移除,React会调用componentWillUnmount()这个钩子函数,定时器也就会被清除。
!注意:
构造函数是唯一能够初始化this.state的地方
// 错误代码:此代码不会重新渲染组件
this.state.comment = 'Hello';
// 正确代码
this.setState({comment: 'Hello'});
异步更新状态
React 可以将多个setState() 调用合并成一个调用来提高性能。
因为 this.props 和 this.state 可能是异步更新的,你不应该依靠它们的值来计算状态。
// 错误代码 : 此代码可能无法更新计时器
this.setState({
counter: this.state.counter + this.props.increment,
});
要修复它,请使用第二种形式的 setState() 来接受一个函数而不是一个对象。该函数将接收先前的状态作为第一个参数,将此次更新被应用时的props做为第二个参数:
// 正确代码
// 常规函数
this.setState( function (preState, props) {
return {
counter: prevState.counter + props.increment
};
});// 箭头函数
this.setState ( function (prevState, props) {
return {
counter: prevState.counter + props.increment
};
});
状态更新合并
当你调用 setState() 时,React 将你提供的对象合并到当前状态。
例如,你的状态可能包含一些独立的变量:
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。
数据自顶向下流动
父组件或子组件都不能知道某个组件是有状态还是无状态,并且它们不应该关系某组件是被定义为一个函数还是一个类。
这就是为什么状态通常被称为局部或封装。除了拥有并设置它的组件外,其它组件不可访问。
组件可以选择将其状态作为属性传递给其子组件:
<h2>It is {this.state.date.tolocaleTimeString()}.</h2>
这也适用于用户定义的组件:
<FormattedDate date={this.state.date} />
FormattedDate 组件将在其属性中接收到 date 值,并且不知道它是来自 Clock状态、还是来自 Clock的属性、亦或手工输入:
function FormattedDate (props) {
return <h2>It is {props.date.toLocaleTimeString()}.</h2>;
}
这通常被称为 自顶向下 或 单向数据流。任何状态始终由某些特定组件所有,并且从该状态导出的任何数据或 UI 只能影响树中 下方的组件。
如果你想象一个组件树作为属性的瀑布,每个组件的状态就像一个额外的水源,它连接在一个r任意点,但也流下来。
为了表明所有组件都是真正隔离的,我们可以创建一个 App组件,它渲染三个Clock:
function App () {
return (
<div>
<Clock />
<Clock />
<Clock />
</div>
);
}ReactDOM.render(
<App />,
document.getElementById('root')
);
每个 Clock 建立自己的定时器并且独立更新。
在 React应用程序中,组件是有状态还是无状态被认为是可能随时间而变化的组件的实现细节。可以在有状态组件中使用无状态组件,反之亦然。
生命周期图解
参考网址: