React中文文档之State and Lifecycle

state 和 生命周期
到目前为止,我们仅仅学习了一种方式来更新UI。
我们调用 'ReactDOM.render()' 来改变输出渲染:
		function tick() {
			const element = (
				<div>
					<h1>Hello, world!</h1>
			      	<h2>It is {new Date().toLocaleTimeString()}.</h2>
				</div>
			);
			ReactDOM.render(
				element,
			    document.getElementById('root')
			);
		}
		setInterval(tick, 1000);

在这一小节,我们将学习如何创建真实地可重用的以及封装的Clock组件。它将建立自己的计时器,并每秒更新自身。
我们可以通过封装来开始,如下:
		function Clock(props) {
			return (
				<div>
					<h1>Hello, world!</h1>
			      	<h2>It is {props.date.toLocaleTimeString()}.</h2>
				</div>
			);
		}
		function tick() {
			ReactDOM.render(
				<Clock date={new Date()} />,
			    document.getElementById('root')
			);
		}
		setInterval(tick, 1000);

然而,它忽略了一个重要的需求:事实上,Clock组件建立一个计时器,并且每秒更新UI,应该是 Clock 组件的一个实现细节.
理想情况下,我们应该只写一次,Clock组件就会更新自身。
		ReactDOM.render(
			<Clock />,
		    document.getElementById('root')
		);

为了实现这个,我们需要添加 'state' 到 Clock 组件。
state同props非常相似,但是它是私有的,而且完全被组件自身控制
我们前面提到过,以类方式,定义的组件,有一些额外的特性。
局部state就是类可用的功能之一


将函数式组件,转换成类组件
你可以传唤一个函数式组件,例如 Clock组件,到一个类组件,通过5个步骤:
1.创建一个同名的ES6类,继承 React.Component
2.添加一个单个的render()空方法
3.移动之前的函数式组件的函数体到render()方法中
4.在render()体中,使用 'this.props' 替代 'props'
5.删除留下的空的函数声明
		class Clock extends React.Component {
		  	render() {
			    return (
			    	<div>
				        <h1>Hello, world!</h1>
				        <h2>It is {this.props.date.toLocaleTimeString()}.</h2>
				    </div>
			    );
			}
		}

此时,Clock组件被定义为一个类,而不是函数
这就让我们可以使用 '附加特性',例如:局部state和生命周期钩子


向类中添加局部state
我们通过3步,将props中的data移动到state:
1.在render()方法中,使用 'this.state.data' 替换 'this.props.date'
		class Clock extends React.Component {
			render() {
			    return (
			     	<div>
				        <h1>Hello, world!</h1>
				        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>	// 这行
			      	</div>
			    );
			}
		}

2.添加类的构造器,分配初始的 'this.state':
		class Clock extends React.Component {
			constructor(props) {
				super(props);
				this.state = {date: new Date()};
			}
			render() {
			    return (
			     	<div>
				        <h1>Hello, world!</h1>
				        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
			      	</div>
			    );
			}
		}

注意:我们如何传入 'props' 给基础构造器(constructor)
			constructor(props) {
				super(props);
				this.state = {date: new Date()};
			}

组件类,应该总是传入 'props' 调用基础构造器
3.从<Clock />元素上,移除 'date' prop
		ReactDOM.render(
			<Clock />,
			document.getElementById('root')
		);

稍后,我们将会给组件重新添加计时器
此时修改后的结果如下:
		class Clock extends React.Component {
			constructor(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')
		);

向类中添加生命周期方法:
在由许多组件组成的应用中,很重要的一点是:当组件被销毁时,释放他们占用的资源。
clock组件首次渲染为dom时,我们设置了一个计时器(set up a timer)。这在React中,被称作 'mounting'
当移除由clock组件产生的dom时,我们清除mount设置的计时器。这在React中,被称作 'unmouting'
我们可以在组件类中,声明一些特殊的方法,当组件 mount 和 unmount 时,来运行我们设置的代码:
		class Clock extends React.Component {
			constructor(props) {
				super(props);
				this.state = {data: new Date()};
			}
			componentDitMount(){


			}
			componentWillUnmount(){
			
			}
			render() {
				return (
					<h1>It is {this.state.date.toLocaleTimeString()}.</h1>
				);	
			}
		}

这些方法被称作 '生命周期 钩子'
组件输出已经被渲染为dom后,运行 'componentDitMount()' 钩子。这是个设置计时器的好地方:
		componentDitMount(){
			this.timerID = setInterval(
				() => this.tick(),
				1000
			);
		}

注意我们是如何在 'this' 对象上,保存 计时器ID的。
this.props由React自身创建,而this.state有特殊的意义,如果你需要存储一些数据,且这些数据并不用于可见输出(the visual output),你可以手动给组件类添加额外的字段
如果你在 render() 中不使用其他字段,则不应该在 state 中添加这些字段
我们将在 'componentWillunmount' 生命周期钩子中,清除计时器
		componentWillunmount() {
			clearInterval(this.timerID);	
		}

最后,我们将实现 tick() 方法,将会每秒运行一次
tick() 方法,使用 'this.setState()' 来更新组件的私有state
		tick() {
			this.setState({
				data: new Date();
			});
		}

正确使用 state
关于setState(),你应该知道3件事:
1.不要直接修改state
例如,下面的代码,将不会重新渲染组件:
			this.state.comment = 'Hello';			// 错误
使用 setState() 来代替:
			this.setState({comment: 'Hello'});		// 正确
2.state的更新,可能异步的
为了性能考虑,React可以在一次更新中,批量执行 setSate() 调用
因为 this.props 和 this.state 可能被异步更新,在计算接下来的state,你不应该依赖它们的值(可能还未被更新)
例如,下面的更新counter的代码可能失败:
			this.setState({
				counter: this.state.counter + this.props.increment;		// 错误
			});

为了修复上面的错误,使用setState()的第二种形式,接收一个函数(function),而非一个对象(object)。函数接收上一次的state作为第一个参数,更新应用时的props作为第二个参数:
			this.setState((prevState, props) => ({
				counter : prevState.counter + props.increment			// 正确
			}));

上面,我们使用了一个 '箭头函数'(arrow function),也可以使用一般的函数:
			this.setState(function(prevState, props) {
				return {
					counter : prevState.counter + props.increment		// 正确
				}
			});

3.state更新会被合并
当调用 'setState()'时,React会合并 你提供的对象(the object you provide) 到当前的state。
例如:提供的state可能包含几个独立的变量:
			constructor(props) {
				super(props);
				this.state = {
					posts: [],
					comments: []
				};
			}

接着,可以使用独立的 setState() 调用来分别更新它们:
			componentDitMount() {
				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


数据流向下传递(the data flows down)
父组件和子组件,都不知道某个组件是 'stateful' 还是 'stateless',而且,它们不关心它们被定义为一个函数还是类。
这就是为什么state经常被称作 局部(local) 或者 包装、封装(encapsulated)。对于除了拥有并设置它的组件之外的任意其他组件,它是不可访问的。
同 props 一样,组件也可以选择,将它的state传递给它的子组件:
		<h2>It is {this.state.date.loLocaleTimeString()}.</h2>
对用户自定义组件也是有效的:
		<FormattedDate data={this.state.date} />
FormattedDate 组件接收props中的date,但是并不知道它是来自'clock' 的state,props,还是手动输入:
		function FormattedDate(props) {
			return <h2>It is {props.date.toLocaleTimeString()}.</h2>;
		}

这通常被称为“自上而下”或“单向”的数据流。任何由某些特定组件拥有的state,任何来自该state的数据或UI,仅仅会影响树形结构中,它们的下级组件.
想象一个组件树是一个props瀑布,每个组件的state就像一个附加的水源,在任意点加入到瀑布中,但是也向下流动。
为了显示所有的组件是完全隔离的,我们可以创建一个app组件,渲染3个 clock 组件:
		function App() {
			return (
				<div>
					<Clock />
					<Clock />
					<Clock />
				</div>
			);
		}
		ReactDOM.render(
			<App />,
			document.getElementById('root')
		);

每个Clock设置它自己的计时器,并且独立的更新。
在React应用中,不论组件是有状态的(stateful),还是无状态的(stateless),都被认为是组件的实现细节,可以随着时间而改变。你可以在有状态的组件中使用无状态组件,反过来也一样。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值