State 与 Props 区别
props 是组件对外的接口,state 是组件对内的接口。组件内可以引用其他组件,组件之间的引用形成了一个树状结构(组件树),如果下层组件需要使用上层组件的数据或方法,上层组件就可以通过下层组件的props属性进行传递,因此props是组件对外的接口。组件除了使用上层组件传递的数据外,自身也可能需要维护管理数据,这就是组件对内的接口state。根据对外接口props 和对内接口state,组件计算出对应界面的UI。

主要区别:

State是可变的,是一组用于反映组件UI变化的状态集合;
而Props对于使用它的组件来说,是只读的,要想修改Props,只能通过该组件的父组件修改。
在组件状态上移的场景中,父组件正是通过子组件的Props, 传递给子组件其所需要的状态。
Props的使用
当一个组件被注入一些属性(Props )值时,属性值来源于它的父级元素,所以人们常说,属性在 React 中是单向流动的:从父级到子元素。

1、props(属性) 默认为 “true”
如果你没给 prop(属性) 传值,那么他默认为 true 。下面两个 JSX 表达式是等价的:

<MyTextBox autocomplete />
<MyTextBox autocomplete={true} />
 
通常情况下,我们不建议使用这种类型,因为这会与ES6中的对象shorthand混淆 。ES6 shorthand 中 {foo} 指的是 {foo: foo} 的简写,而不是 {foo: true} 。这种行为只是为了与 HTML 的行为相匹配。
(举个例子,在 HTML 中,< input type=“radio” value=“1” disabled /> 与 < input type=“radio” value=“1” disabled=“true” /> 是等价的。JSX 中的这种行为就是为了匹配 HTML 的行为。)

2、props扩展
如果你已经有一个 object 类型的 props,并且希望在 JSX 中传入,你可以使用扩展操作符 … 传入整个 props 对象。这两个组件是等效的:

function App1() {
return <Greeting firstName="Ben" lastName="Hector" />;
}

function App2() {
const props = {firstName: 'Ben', lastName: 'Hector'};
return <Greeting {...props} />;
}
 
显然下面的方法更方便:因为它将数据进行了包装,而且还简化了赋值的书写

State
一、State是什么?
React 的核心思想是组件化,而组件中最重要的概念是State(状态),State是一个组件的UI数据模型,是组件渲染时的数据依据。

状态(state) 和 属性(props) 类似,都是一个组件所需要的一些数据集合,但是state是私有的,可以认为state是组件的“私有属性(或者是局部属性)”。

如何判断是否为State ?
组件中用到的一个变量是不是应该作为组件State,可以通过下面的4条依据进行判断:

这个变量是否是通过Props从父组件中获取?如果是,那么它不是一个状态。
这个变量是否在组件的整个生命周期中都保持不变?如果是,那么它不是一个状态。
这个变量是否可以通过其他状态(State)或者属性(Props)计算得到?如果是,那么它不是一个状态。
这个变量是否在组件的render方法中使用?如果不是,那么它不是一个状态。这种情况下,这个变量更适合定义为组件的一个普通属性,例如组件中用到的定时器,就应该直接定义为this.timer,而不是this.state.timer。
并不是组件中用到的所有变量都是组件的状态!当存在多个组件共同依赖一个状态时,一般的做法是状态上移,将这个状态放到这几个组件的公共父组件中。

二、如何正确使用 State
1、用setState 修改State

直接修改state,组件并不会重新触发render()

// 错误
this.state.comment = 'Hello';

正确的修改方式是使用setState()

// 正确
this.setState({comment: 'Hello'});

2、State 的更新是异步的

调用setState后,setState会把要修改的状态放入一个队列中(因而 组件的state并不会立即改变);
之后React 会优化真正的执行时机,来优化性能,所以优化过程中有可能会将多个 setState 的状态修改合并为一次状态修改,因而state更新可能是异步的。
所以不要依赖当前的State,计算下个State。当真正执行状态修改时,依赖的this.state并不能保证是最新的State,因为React会把多次State的修改合并成一次,这时,this.state将还是这几次State修改前的State。
另外需要注意的事,同样不能依赖当前的Props计算下个状态,因为Props一般也是从父组件的State中获取,依然无法确定在组件状态更新时的值。
综上所述:
this.props 和 this.state 可能是异步更新的,你不能依赖他们的值计算下一个state(状态)

例:
这样 counter(计数器)会更新失败

// 错误
this.setState({
counter: this.state.counter + this.props.increment,
});

要弥补这个问题,使用 setState() 的另一种形式,它接受一个函数而不是一个对象。这个函数有两个参数:
(1)第一个参数: 是当前最新状态的前一个状态(本次组件状态修改前的状态)
(2)第二个参数:是当前最新的属性props

// 正确
this.setState((prevState, props) => ({
counter: prevState.counter + props.increment
}));

//注意:下面这样是错的
this.setState((prevState, props) => { //没将{}用()括起来,所以会解析成代码块
counter: prevState.counter + props.increment
});

如果你还不懂没关系,看下面例子:
我们现在渲染出一个button,想每点击一下,counter就+3
看下面代码:

class App extends React.Component {
state = {
counter: 0,
}
handleClick = () => {
const { counter } = this.state;
//或者 const counter = this.state.counter;
this.setState({ counter: counter + 1 });
this.setState({ counter: counter + 1 });
this.setState({ counter: counter + 1 });
}
render() {
return (
<div>
counter is: {this.state.counter}
<button onClick={this.handleClick} >点我</button>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('root'));

输出:

每点击一下,加+1,并不是+3

之所以+1,不是+3,是因为 state 的更新可能是异步的,React 会把传入多个 setState的多个 Object “batch” 起来合并成一个。合并成一个就相当于把传入 setState 的多个 Object 进行 shallow merge,像这样:

const update = {
counter: counter + 1,
counter: counter + 1,
counter: counter + 1
//因为上面三句话都一样,所以会当一句话执行
}

我们可以这么做就会成功:看下面

class App extends React.Component {
state = {
counter: 0,
}
handleClick = () => {
this.setState(prev => ({ counter: prev.counter + 1 }));
this.setState(prev => ({ counter: prev.counter + 1 }));
this.setState(prev => ({ counter: prev.counter + 1 }));
//这样是错的 this.setState(prev => {counter: prev.counter + 1});
//这样是错的 this.setState(prev => {counter:++prev.counter});
//这样是错的 this.setState(prev => {counter:prev.counter++});
}
render() {
return (
<div>
counter is: {this.state.counter}
<button onClick={this.handleClick} >点我</button>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('root'));

之所以成功是因为:传入多个 setState 的多个 Object 会被 shallow Merge,而传入多个 setState 的多个 function 会被 "queue" 起来,queue 里的 function 接收到的 state(上面是 prev )都是前一个 function 操作过的 state。

3、State更新会被合并
官方文档看不懂不要紧,直接举个例子你就懂了。

例如一个组件的状态为:

this.state = {
title : 'React',
content : 'React is an wonderful JS library!'
}

当只需要修改状态title时,只需要将修改后的title传给setState:

this.setState({title: 'Reactjs'});
1
React会合并新的title到原来的组件状态中,同时保留原有的状态content,合并后的State为:

{
title : 'Reactjs',
content : 'React is an wonderful JS library!'
}

4、setState里顺序更新

//history 为数组
this.setState({
history: history.concat([1]), //(1)
current: history.length, //(2)
nextPlayer: !nextPlayer, //(3)
});

执行setState时:先更新history,然后再用更新改变后的history计算current的值,最后再更新nextPlayer

三、根据State类型 更新
当状态发生变化时,如何创建新的状态?根据状态的类型,可以分成三种情况:

1、 状态的类型是不可变类型(数字,字符串,布尔值,null, undefined)

这种情况最简单,直接给要修改的状态赋一个新值即可

//原state
this.state = {
count: 0,
title : 'React',
success:false
}
//改变state
this.setState({
count: 1,
title: 'bty',
success: true
})

2、 状态的类型是数组
数组是一个引用,React 执行 diff 算法时比较的是两个引用,而不是引用的对象。所以直接修改原对象,引用值不发生改变的话,React 不会重新渲染。因此,修改状态的数组或对象时,要返回一个新的数组或对象。
(1)增加
如有一个数组类型的状态books,当向books中增加一本书(chinese)时,使用数组的concat方法或ES6的数组扩展语法

// 方法一:将state先赋值给另外的变量,然后使用concat创建新数组
let books = this.state.books;
this.setState({
books: books.concat(['chinese'])
})

// 方法二:使用preState、concat创建新数组
this.setState(preState => ({
books: preState.books.concat(['chinese'])
}))

// 方法三:ES6 spread syntax
this.setState(preState => ({
books: [...preState.books, 'chinese']
}))

(2)截取
当从books中截取部分元素作为新状态时,使用数组的slice方法:

// 方法一:将state先赋值给另外的变量,然后使用slice创建新数组
let books = this.state.books;
this.setState({
books: books.slice(1,3)
})
//
// 方法二:使用preState、slice创建新数组
this.setState(preState => ({
books: preState.books.slice(1,3)
}))

(3)条件过滤
当从books中过滤部分元素后,作为新状态时,使用数组的filter方法:

// 方法一:将state先赋值给另外的变量,然后使用filter创建新数组
var books = this.state.books;
this.setState({
books: books.filter(item => {
return item != 'React';
})
})

// 方法二:使用preState、filter创建新数组
this.setState(preState => ({
books: preState.books.filter(item => {
return item != 'React';
})
}))

注意:不要使用push、pop、shift、unshift、splice等方法修改数组类型的状态,因为这些方法都是在原数组的基础上修改,而concat、slice、filter会返回一个新的数组。

3、状态的类型是普通对象(不包含字符串、数组)
对象是一个引用,React 执行 diff 算法时比较的是两个引用,而不是引用的对象。所以直接修改原对象,引用值不发生改变的话,React 不会重新渲染。因此,修改状态的数组或对象时,要返回一个新的对象。
使用ES6 的Object.assgin方法

// 方法一:将state先赋值给另外的变量,然后使用Object.assign创建新对象
var owner = this.state.owner;
this.setState({
owner: Object.assign({}, owner, {name: 'Jason'})
})

// 方法二:使用preState、Object.assign创建新对象
this.setState(preState => ({
owner: Object.assign({}, preState.owner, {name: 'Jason'})
}))

使用对象扩展语法(object spread properties)

// 方法一:将state先赋值给另外的变量,然后使用对象扩展语法创建新对象
var owner = this.state.owner;
this.setState({
owner: {...owner, name: 'Jason'}
})

// 方法二:使用preState、对象扩展语法创建新对象
this.setState(preState => ({
owner: {...preState.owner, name: 'Jason'}
}))

综上所述:
创建新的状态对象的关键是,避免使用会直接修改原对象的方法,而是使用可以返回一个新对象的方法。

四、State向下流动
我们说 props 是组件对外的接口,state 是组件对内的接口。
一个组件可以选择将 state(状态) 向下传递,作为其子组件的 props(属性):

<MyComponent title={this.state.title}/>
1
这通常称为一个“从上到下”,或者“单向”的数据流。任何 state(状态) 始终由某个特定组件所有,并且从该 state(状态) 导出的任何数据 或 UI 只能影响树中 “下方” 的组件。

如果把组件树想像为 props(属性) 的瀑布,所有组件的 state(状态) 就如同一个额外的水源汇入主流,且只能随着主流的方向向下流动。

你不知道的props和state的更多相关文章

  1. react native中对props和state的理解

    最近使用react native这个新的技术做完一个项目,所以赶紧写个博客巩固一下. 今天我想说的是props和state,当然这是我个人的理解,如果有什么不对的地方,望指正. 首先我先说说props ...

  2. props 和 state的区别

    作者:孙志勇 微博 日期:2016年11月29日 一.时效性 所有信息都具有时效性.文章的价值,往往跟时间有很大关联.特别是技术类文章,请注意本文创建时间,如果本文过于久远,请读者酌情考量,莫要浪费时 ...

  3. 关于props和state以及redux中的state

    React的数据模型分为共有数据和私有数据,共有数据可以在组件间进行传递,私有数据为当前组件私有.共有数据在React中使用props对象来调用,它包含标签所有的属性名称和属性值,props对象有三个 ...

  4. React 深入系列3:Props 和 State

    文:徐超,<React进阶之路>作者 授权发布,转载请注明作者及出处 React 深入系列3:Props 和 State React 深入系列,深入讲解了React中的重点概念.特性和模式 ...

  5. ReactNative之从HelloWorld中看环境搭建、组件封装、Props及State

    开篇呢,先给大家问个好,今天是中秋节,祝大家中秋节快乐!!虽然是中秋节,但是木有回家还是总结一下知识点写写博客吧,想着昨天总结一下的,但是昨天和几个同学小聚了一下,酒逢知己总是千杯少呢,喝的微醺不适合 ...

  6. React Native基础&入门教程:以一个To Do List小例子,看props和state

    本文由葡萄城技术团队于博客园原创并首发 转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具.解决方案和服务,赋能开发者. 在上篇中,我们介绍了什么是Flexbox布局,以及如何使用Flexb ...

  7. React中props和state相同点和不同点

    朋友们,我想死你们了,最近这几天忙着和病魔作斗争所以没怎么写博客,今天感觉好点了,赶紧来写一波,就是这木敬业. 今天我们来讨论讨论props和state相同点和不同点 首先我来概要说明一下这两者 pr ...

  8. React中Props 和 State用法

    React中Props 和 State用法 1.本质 一句话概括,props 是组件对外的接口,state 是组件对内的接口.组件内可以引用其他组件,组件之间的引用形成了一个树状结构(组件树),如果下 ...

  9. React中props与state

    以下内容均为个人理解. 1.state: 在react中,state可以看成管理页面状态的集合(实则一个对象而已),库里面的成员均为页面渲染变量,整个页面为一个状态机,当state发生变化时,页面会重 ...

随机推荐

  1. Burpsuiet爆破

    burpsuite中intruder标签内attack type四种类型的用法和区别 2016年07月24日 18:13:26 xss_01 阅读数:9802更多 个人分类: burpsuite网络安 ...

  2. DVWA--CSP Bypass

    0x01看到标题,是否有点疑惑 CPS 是什么东东.简单介绍一下就是浏览器的安全策略,如果 标签,或者是服务器中返回 HTTP 头中有 Content-Security-Policy 标签 ,浏览器会 ...

  3. 7.并发编程--多线程通信-wait-notify

    并发编程--多线程通信-wait-notify 多线程通信:线程通信的目的是为了能够让线程之间相互发送信号; 1. 多线程通信: 线程通信的目的是为了能够让线程之间相互发送信号.另外,线程通信还能够使 ...

  4. 用命令行编译运行java文件的乱码问题

    之前在写的时候没有遇到过这个问题,用惯了eclipse之后突然用Notepad++就出现乱码了 我在编写的时候 指定Noepad++的编码是 UTF-8编码,然后进入命令行,编译的时候就出现了乱码 然 ...

  5. redis的安装及使用总结

    Windows版本的安装 下载地址:https://www.jb51.net/softs/541181.html 安装过程 把压缩包内的文件解压到非中文目录即可 启动redis 启动redis要通过命 ...

  6. 在linux下搭建go环境

    这几天小Jerry开始接触Go语言了,因为小Jerry学个东西必须要从最基础的开始弄懂,不然~她理解不了<hahaha> 所以,今天就来讲最基础,却也很容易让小Jerry这样的菜鸟感到困扰 ...

  7. webpack.config.js文件

    与 package.json 配合使用 var path=require("path");var OpenBrowserPlugin = require('open-browser ...

  8. 一、Python环境的搭建

    1.python官方下载地址:https://www.python.org/:python现在有两个版本:python2.7.x和python3.x 2.安装:一路下一步,默认安装 3.配置环境变量: ...

  9. centos7 ngxin启动失败:Job for nginx.service failed(80端口被占用的解决办法)

    问题描述:(flaskApi) [root@67 flaskDemo]# service nginx start Redirecting to /bin/systemctl start nginx.s ...

  10. Spring mvc注解说明

    编号 注解 说明 位置 备注 1 @Controller 将类变成Spring Bean 类 现阶段 @Controller . @Service 以及 @Repository 和 @Componen ...