React入门系列 – 3 state与props

34次阅读

共计 5247 个字符,预计需要花费 14 分钟才能阅读完成。

3.1 什么是 state
我们要认识到,React 中的组件其实是一个函数,所以 state 是函数内部的私有变量,外部其他组件或者方法都是无法直接访问到内部的 state。而 state 主要被设计用于维持组件内部私有状态。
3.1.1 初始化 state
初始化 state 需要在 class 中 constructor 进行。
import React, {PureComponent} from ‘react’

export default class index extends PureComponent {
constructor(props){
super(props)
this.state = {name:’demo react’,time:new Date()}
}
render() {
return (
<div>
Hello world React!{this.state.name}
<p> 组件生成时间:{this.state.time}</p>
</div>
)
}
}

在这个代码中,我们初始化了 name,time 两个 state 值。一旦 state 初始化完成,我们就可以在 render 中进行访问。
使用 npm run dev 命令运行,并在浏览器中打开查看吧。
3.1.2 修改 state 值 setState
我们已经知道了如何初始化组件中的 state 值,那么接下来我们来看看,如何实现修改 state 的值
import React, {PureComponent} from ‘react’

export default class index extends PureComponent {
constructor(props){
super(props)
this.state = {name:’demo react’,time:+new Date()}
}
handleUpdateName () {
this.state.time = (+new Date())
}
render() {
return (
<div>
Hello world React!{this.state.name}
<p> 组件生成时间:{this.state.time}</p>
<button onClick={this.handleUpdateName.bind(this)}> 修改值 </button>
</div>
)
}
}
有些动作快的同学,第一个想法就是如此修改组件中的 state 值,但是值得玩味的是,值的确是修改成功了,但是,并没有实时体现在界面上。这究竟是怎么回事呢?
这就要来看看,React 中的 setState 方法。
React 对于 setState 的定义为请求 React 修改某个数据,而 React 的实现则是将对变量的修改放入一个修改队列中,在一个循环之后进行批量更新结果(深入点涉及 VDom 的更新机制)。所以,这里会造成一个问题,就是 setState 数据之后立刻进行读取,可能你读取到的数据,并非是已经被更新过的有效值。
setState 有三种修改数据的方式,让我们来一个一个尝试。
3.1.2.1 直接赋值
import React, {PureComponent} from ‘react’

export default class index extends PureComponent {
constructor(props){
super(props)
this.state = {name:’demo react’,time:+new Date()}
}
handleUpdateName () {
// this.state.time = (+new Date())
console.log(‘ 修改前的值 ’,this.state.time)
this.setState({time:+new Date()})
console.log(‘ 修改后的值 ’,this.state.time)
let that = this
setTimeout(function(){
console.log(‘ 当前 state 值 ’,that.state.time)
})
}
render() {
return (
<div>
Hello world React!{this.state.name}
<p> 组件生成时间:{this.state.time}</p>
<button onClick={this.handleUpdateName.bind(this)}> 修改值 </button>
</div>
)
}
}

点击按钮,控制台输出了不同的值,可以观察到 setState 是采用异步队列模式的。

3.1.2.2 赋值完成等待同步完成之后执行回调
现在出现了一个问题,如果我们需要通过等待 setState 修改完成的值之后,应该如何处理?React 为我们的 setState 提供了第二个参数 callback。
import React, {PureComponent} from ‘react’

export default class index extends PureComponent {
constructor(props){
super(props)
this.state = {name:’demo react’,time:+new Date()}
}
handleUpdateName () {
// this.state.time = (+new Date())
console.log(‘ 修改前的值 ’,this.state.time)
this.setState({time:+new Date()},(function(){
console.log(‘ 当前 state 值 ’,that.state.time)
})
console.log(‘ 修改后的值 ’,this.state.time)
}
render() {
return (
<div>
Hello world React!{this.state.name}
<p> 组件生成时间:{this.state.time}</p>
<button onClick={this.handleUpdateName.bind(this)}> 修改值 </button>
</div>
)
}
}

再次运行,并且点击按钮。我们可以看到控制台输出的值是跟第一个方法是一致的。
现在我们来进行一次脑暴,可不可以直接修改 state 值呢? 因为我们在最早直接对 state 修改的时候,React 并未关闭这个对象的 set 方法。那么我们可否直接通过修改 state 来进行渲染呢?React 中的一个方法为我们解决了这个疑问。
import React, {PureComponent} from ‘react’

export default class index extends PureComponent {
constructor(props){
super(props)
this.state = {name:’demo react’,time:+new Date()}
}
handleUpdateName () {
console.log(“ 修改前的值 ”, this.state.time);
this.state.time = (+new Date())
console.log(“ 修改后的值 ”, this.state.time);
console.log(“ 当前 state 值 ”, this.state.time);
this.forceUpdate()
}
render() {
return (
<div>
Hello world React!{this.state.name}
<p> 组件生成时间:{this.state.time}</p>
<button onClick={this.handleUpdateName.bind(this)}> 修改值 </button>
</div>
)
}
}
上面这个代码仅仅用于脑暴,参考,不要在生产环境中使用,因为这个会造成 React 渲染算法与各种 Hook 失效,造成全局重新渲染。
3.1.2.3 通过原始数据进行更新
在某些场景下面,我们可能是新的值是基于上一次的值推算而来,所以 React 提供了 setState 传递进方法来进行推算处理。
import React, {PureComponent} from “react”;

export default class index extends PureComponent {
constructor(props) {
super(props);
this.state = {name: “demo react”, time: +new Date() };
}
handleUpdateName() {
console.log(“ 修改前的值 ”, this.state.time);
let that = this;
this.setState(oldData => {
return {time: oldData.time + 1000};
});
console.log(“ 修改后的值 ”, this.state.time);
setTimeout(function() {
console.log(“ 当前 state 值 ”, that.state.time);
});
}
render() {
return (
<div>
Hello world React!{this.state.name}
<p> 组件生成时间:{this.state.time}</p>
<button onClick={this.handleUpdateName.bind(this)}> 修改值 </button>
</div>
);
}
}

最后说一点,就是 setState 是浅拷贝, 如果是结构比较深层的对象,很多同学会采用 JSON.string() 这种来进行深拷贝,这样的操作虽然说是可以的,但是非常影响性能。
React 推出了 immutable 与 15 版本之后推出来的 PureComponent 就是为了解决这些问题的。有兴趣的同学可以搜索一下相关资料进行拓展阅读。
3.2 什么是 props
我们知道,在函数中有带参数的方法,那么 props 其实就是传入方法中的参数。并且在 React 中 props 是只读属性。在使用场景上,是由父组件向子组件传递值的时候使用的。
3.2.1 接收并渲染 props 值
我们首先创建一个可以接收 props 值的组件。
import React, {PureComponent} from “react”;

export default class index extends PureComponent {
constructor(props) {
super(props);
}

render() {
return (
<div style={{‘background’:’#fefefe’}}>
{this.props.value||’ 暂无数据 ’}
</div>
);
}
}
接着,我们修改 Index.js 引用这个组件。
import React, {PureComponent} from “react”;
import Content from ‘./content.js’
export default class index extends PureComponent {
constructor(props) {
super(props);
this.state = {name: “demo react”, time: +new Date() };
}
handleUpdateName() {
this.setState({time:+new Date()})
}
render() {
return (
<div>
Hello world React!{this.state.name}
<p> 组件生成时间:{this.state.time}</p>
<button onClick={this.handleUpdateName.bind(this)}> 修改值 </button>
<Content/>
</div>
);
}
}

这里大家有一点要注意,在 React 中,所有的组件名称第一个字母都必须大写,这是为了与 html 标签区分出来。
如何向子组件传值呢?就像给 html 标签增加属性一样。
<Content value={‘ 我设置了 ’ + this.state.time}/>
这样,组件内部可以通过 props 读取到 value 值了。不过 React 的组件传递中有一个很有趣的属性 children,这个的用处传递组件包含的内容。
继续修改引入组件的代码,如下
// index.js
<Content value={‘ 我设置了 ’ + this.state.time} > 主体 Children</Content>
import React, {PureComponent} from “react”;

export default class index extends PureComponent {
constructor(props) {
super(props);
}

render() {
return (
<div style={{‘background’:’#fefefe’}}>
{this.props.value||’ 暂无数据 ’},children:{this.props.children}
</div>
);
}
}

最终显示效果就是这样的

原文地址:https://www.yodfz.com/detail/…

正文完
 0