40. B站 react三大属性1:state - yiqunkeke/react-jianshu-shangguigu-zty GitHub Wiki
document.createElement ----> 创建真实DOM
React.createElement ---> 创建虚拟DOM
const VDOM = React.createElement('h1', { id: 'title'}, 'hello, react')
const VDOM2 = React.createElement('h1', { id: 'title'}, React.createElement('span', {}, 'hello, react'))
是原生js创建的语法糖
const VOM = (
<h1>
<span>hello, react</span>
</h1>
)
- 本质是Object类型的对象(一般对象)
- 虚拟DOM比较“轻”,真实DOM比较“重”,因为虚拟DOM是 Reac内部在用,无需真实DOM上那么多的属性
- 虚拟DOM最终会被 React转化为真实DOM,呈现在页面上。
- XML 早期 用于存储和传输数据
<student>
<name>Tom</name>
<age>19</age>
</student>
后来这种方式逐渐被JSON替换。原因是,xml存储数据的方式,结构代码比数据代码居然占的还要多。
- JSON
"{"name": "TOM", "age": 19}"
用JSON存储数据,更一目了然,而且JSON提供了2个非常好用的API. JSON.parse()
和 JSON.stringfy()
-
jsx语法规则
- 样式的类名指定不要用class,要用className
<style> .title { background: yellow} </style> const VOM = ( <h2 className="title">hello,react</h2> )
-
标签中混入 JS表达式 要用 {}
-
style内联样式使用双{{}},属性名小驼峰命名
const VOM = ( <h2 style={{color: 'white', fontSize: '29px'}}>hello,react</h2> )
- 只能有一个根标签
const VOM = ( <div> <h2 style={{color: 'white', fontSize: '29px'}}>hello,react</h2> <input type="text"/> </div> )
-
标签必须闭合
<input>
错误<input/>
正确 -
关于标签首字母
- 若小写字母开头,则将该标签转为html中同名元素;若html中无该标签对应的同名元素,则报错。
- 若大写字母开头,react就去渲染对应的组件,若组件没有定义,则报错。
-
{ } 中只能写表达式。 一定区分 【js语句】和 【js表达式】
- 表达式:一个表达式会产生一个值,可以主在任何王玲上需要值的地方。
// 下面这些都是表达式 a a+b demo(1) // 函数调用表达式 arr.map() // map用于加工数组,返回一个新数组 function test() {}
- 语句(代码)
// 下面这些都是语句 if() {} for() {} switch() { case: xxx }
-
所以在{ }中不能写for循环,不能写if..else,不能写switch..case
<script type="text/babel"> // 模拟一些数据 const data = ['Angular', 'React', 'Vue'] // 1.创建虚拟DOM const VDOM = ( <div> <h1>前端js框架列表</h1> <ul> { for() // 报错 } </ul> </div> ) // 2.渲染虚拟DOM到页面 ReactDOM.render(VDOM, document.getElementById('test')) </script>
在{ }中只能写表达式
<script type="text/babel"> // 模拟一些数据 const data = ['Angular', 'React', 'Vue'] // 1.创建虚拟DOM const VDOM = ( <div> <h1>前端js框架列表</h1> <ul> { data.map((item, index) => { return <li key={index}> {item} </li> }) } </ul> </div> ) // 2.渲染虚拟DOM到页面 ReactDOM.render(VDOM, document.getElementById('test')) </script>
并且,如果你在{}中写了一个数组,react会帮你把数组自动进行遍历。但如果你写一个对象,react就无能为力了。
模块
- 理解:向外提供特定功能的js程序,一般就是一个js文件。
- 为什么要拆成模块: 随着业务逻辑增加,代码越来越多且复杂
- 作用: 复用js,简化js的编写,提高js运行效率
组件
- 理解:用来实现局部功能效果的代码和资源的集合(html/css/js/image等等)
- 为什么:一个界面的功能更复杂
- 作用: 复用编码,简化项目编码,提高运行效率
模块化
- 当应用的js都以模块来编写的,这个应用就是一个模块化的应用
组件化
- 当应用是以多组件 的方式实现,这个应用就是一个组件化的应用
回顾组件的定义,是代码和资源的集合,包含 html, css, js, font, video...等等
总结:组件最少也得有结构
<script type="text/babel">
// 1. 创建函数式组件
// function demo() {
// return <h2>我是用函数定义的组件(适用于【简单组件】的定义)</h2>
// }
function Demo() {
console.log(this) // 此处的this是undefined,因为babel编译后开启了严格模式
return <h2>我是用函数定义的组件(适用于【简单组件】的定义)</h2>
}
// 2. 渲染组件到页面
// ReactDOM.render(demo, document.getElementById('test'))
// 报错:Warning: Functions are not valid as a React child.(函数类型不能做为 react 的节点)This may happen if you return a Component instead of <Component /> from render. Or maybe you meant to call this function rather than return it.
// 要写demo标签
// ReactDOM.render(<demo/>, document.getElementById('test'))
// 报错:Warning: The tag <demo> is unrecognized in this browser. (浏览器不识别demo元素)If you meant to render a React component, start its name with an uppercase letter.(组件首字母需大写)
ReactDOM.render(<Demo/>, document.getElementById('test'))
</script>
首字母大写、闭合、组件写成标签。
函数式组件,函数名就是组件名。
执行了 ReactDOM.render(, document.getElementById('test'))之后,发生了什么?
- React 解析组件标签,找到了 组件
- 发现组件是使用函数定义的,随后调用该函数,将返回的虚拟DOM转为真实DOM,随后呈现在页面中
<script type="text/javascript">
// 创建一个Person类
class Person {
// 构造器方法
constructor(name, age) {
// 构造器中的this是谁? ---类的实例对象
this.name = name
this.age = age
}
// 一般方法
speak() {
// speak方法放在了哪里?----类的原型对象上,供实例调用
// 通过Person实例调用speak时,speak中的this就是Person实例
console.log(`我叫${this.name},年龄是${this.age}`)
}
}
// 创建一个Person的实例对象
const p1 = new Person('tom', 18)
const p2 = new Person('jerry',19)
console.log(p1) // Person {name: 'tom', age: 18}
console.log(p2) // Person {name: 'jerry', age: 19}
p1.speak()
p2.speak()
</script>
总结 :
-
类中的构造器不是必须写的。要对实例进行一些初始化的操作,如添加指定属性时才写。
-
如果A类继承了B类,且A类中写了构造器,那么A类构造器中的super是必须要调用的。
-
类中的定义的方法,都是放在类的原型对象上,供实例使用。
类式组件,类名就是组件名
<script type="text/babel">
// 1.创建类式组件
class MyComponent extends React.Component{
render() {
// render()是放在哪里的?----MyComponent的原型对象上,供实例使用。问题:实例在哪呢?
// render中的this是谁? -----MyComponent这个类的实例对象。<=>或者叫 MyComponent组件的实例对象。简称 MyComponent组件实例对象
// 注意 render中的this指向的不是MyComponent类,而是指向通过MyComponent类new出来的实例对象。即组件实例对象。
console.log('render中的this': this) // MyComponnet {....}
return <h2>我是用类定义的组件,(适用于【复杂组件】的定义)</h2>
}
}
// 2.渲染组件到页面
ReactDOM.render(<MyComponent/>, document.getElementById('test'))
// 当你执行了ReactDOM.render(<MyComponent/>, document.getElementById('test'))之后,发生了什么?
// 1. React解析组件标签,找到了MyComponent组件
// 2. 发现组件是使用类定义的,随后new出来该类的实例,并通过该实例调用到原型上的render方法。
// 3. 将render返回的虚拟DOM转为真实DOM,随后呈现在页面中。
</script>
-
【复杂组件】--有状态的组件叫复杂组件。状态,就是 state。
-
[简单组件]--无状态的组件
-
状态
人 ----> 状态 ---> 影响 ---> 行为
组件 ----> 状态 ----> 驱动 ---> 页面
注意这里是【组件实例】的三大属性。由于函数式组件中,并没有实例对象。所以这里讲的3大属性,都是针对类式组件的。
类式组件中是有 组件实例对象的。我们讲的3大属性就是组件实例对象中的属性。
<script type="text/babel">
// 1. 创建组件
class Weather extends React.Component {
// 写构造器。
// 构造器能接收到什么?取决于new Weather时传的什么。
// 可是,new Weather这个动作,是由React帮我们做的。---->只能去参考官方文档。
// 官网上首页-有状态组件--代码:构造器能接收到props
constructor(props) {
super(props) // 在类中,如果该类继承自,且写了constructor,则必须调一下super。
// 如果不调用super,则报错:missing Super() call in construtor
// 写构造器的目的是什么?在类的实例对象上添加一些属性
this.state = {isHot: true}
}
render() {
console.log(this) // this 就是Weather类的实例对象【Weather的组件实例对象】。如果你想在类的实例对象上添加一些属性,就需要借助constructor构造器
const { isHot } = this.state
return <h1>今天天气很{isHot ? '炎热' : '凉爽'}</h1>
}
}
// 2. 渲染组件到页面
ReactDOM.render(<Weather/>, document.getElementById('test'))
</script>
总结:上面就是借助构造器初始化状态。以及读取组件实例对象中的状态。
<body>
<button id="btn1">按钮1</button>
<button id="btn2">按钮2</button>
<button onclick="demo()">按钮3</button>
<script type="text/javascript">
// 方式1
const btn1 = document.getElementById('btn1')
btn1.addEventListener('click', () => {
alert('按钮1被点击了')
})
// 方式2
const btn2 = document.getElementById('btn2')
btn2.onclick = () => {
alert('按钮2被点击了')
}
// 方式3
function demo() {
alert('按钮3被点击了')
}
</script>
</body>
在React中,上面3种方式都可以用。但是更推荐第三种。
<script type="text/babel">
class Weather extends React.Component {
constructor(props) {
super(props)
this.state = {isHot: true}
}
render() {
const { isHot } = this.state
return <h1 id="title">今天天气很{isHot ? '炎热' : '凉爽'}</h1>
}
}
ReactDOM.render(<Weather/>, document.getElementById('test'))
// 方式1
// const title = document.getElementById('title') // 获取节点
// title.addEventListener('click', () => { // 绑定事件
// console.log('标题被点击了')
// })
// 方式2
const title = document.getElementById('title')
title.onclick(() => {
console.log('标题被点击了')
})
// 虽然这两种方式都可以实现事件绑定。但是尽量不这么用。这种满屏都是document操作。
</script>
在react中,尽量用第三种方式
<script type="text/babel">
class Weather extends React.Component {
constructor(props) {
super(props)
this.state = {isHot: true}
}
render() {
const { isHot } = this.state
return <h1 onclick="demo()">今天天气很{isHot ? '炎热' : '凉爽'}</h1>
}
}
ReactDOM.render(<Weather/>, document.getElementById('test'))
function demo() {
console.log('标题被点击了')
}
// 报错了: Invalid handler property `onclick` Did you mean `onClick` ?
</script>
在react中,把所有的原生事件进行了重写。把原来的小写,改成大写。如 onclick ---> onClick、 onblur ---> onBlur
<script type="text/babel">
class Weather extends React.Component {
constructor(props) {
super(props)
this.state = {isHot: true}
}
render() {
const { isHot } = this.state
return <h1 onClick="demo()">今天天气很{isHot ? '炎热' : '凉爽'}</h1>
}
}
ReactDOM.render(<Weather/>, document.getElementById('test'))
function demo() {
console.log('标题被点击了')
}
// 报错了: Expected `onClick` listener to be a function, instead got a value of `string` type。(onClick事件的监听必须是一个函数,不能是一个字符串)
</script>
在react中,事件绑定后面必须是一个函数。
<script type="text/babel">
class Weather extends React.Component {
constructor(props) {
super(props)
this.state = {isHot: true}
}
render() {
const { isHot } = this.state
return <h1 onClick={demo()}>今天天气很{isHot ? '炎热' : '凉爽'}</h1>
}
}
ReactDOM.render(<Weather/>, document.getElementById('test'))
function demo() {
console.log('标题被点击了')
}
// 有问题:现在还没点击 ,控制台却出现'标题被点击了'。为什么?
</script>
React在渲染组件的时候,帮你new了一个Weather组件实例。通过实例调用到了render。 想拿到render返回值就会执行<h1 onClick={demo()}>今天天气很{isHot ? '炎热' : '凉爽'}</h1>
代码。所以demo()会被 执行。因为我们onClick={demo()}
这样写的{ }中是一个函数调用表达式
。即我们把demo()函数的返回值赋值给了onClick。demo()函数有返回值,是undefined。
所以刚才有输出是因为我们加了(),导致demo函数被调用了的原因导致的。
<script type="text/babel">
class Weather extends React.Component {
constructor(props) {
super(props)
this.state = {isHot: true}
}
render() {
const { isHot } = this.state
return <h1 onClick={demo}>今天天气很{isHot ? '炎热' : '凉爽'}</h1>
}
}
ReactDOM.render(<Weather/>, document.getElementById('test'))
function demo() {
console.log('标题被点击了')
}
</script>
需求:点击标题时,修改state中值
<script type="text/babel">
// 自定义类式组件
class Weather extends React.Component {
constructor(props) {
super(props)
this.state = {isHot: true}
}
render() {
const { isHot } = this.state
return <h1 onClick={demo}>今天天气很{isHot ? '炎热' : '凉爽'}</h1>
}
}
// 渲染组件
ReactDOM.render(<Weather/>, document.getElementById('test'))
// 自定义函数
function demo() {
// console.log('此处修改isHot的值')
// 拿到isHot的值直接修改不就完事了么?
const { isHot } = this.state
console.log(isHot)
}
// 报错:Cannot read property 'state' of undefined(不能够读取state,在undefined身上)
// 所以说是xx.state的左侧xx出问题了。所以this出问题了。
// 注意,demo是自定义函数,babel禁止自定义中的this指向window。所以babel默认开启了严格模式。this指向了undefined 。
</script>
注意,demo是自定义函数,babel默认开启了严格模式,this指向了undefined 。
<!--默认this指向window-->
<script type="text/javascript">
function demo() {
// this 指向 window
console.log(this)
}
demo()
</script>
开启严格模式后,this指向 undefined
<!--开启严格模式后,this指向 undefined-->
<script type="text/javascript">
function demo() {
'use strict'
// this 指向 undefined
console.log(this)
}
demo()
</script>
总结:严格模式下, this指向undefined。非严格模式下,this 指向window。
不管this指向的是undefined还是 window,this下面都没有state属性。所以,上面一开始会报错:Cannot read property 'state' of undefined(不能够读取state,在undefined身上)
<script type="text/babel">
class Weather extends React.Component {
// 构造器中this指向组件实例对象
constructor(props) {
super(props)
this.state = {isHot: true}
}
// render中也可以读取到组件实例对象
render() {
const { isHot } = this.state
return <h1 onClick={demo}>今天天气很{isHot ? '炎热' : '凉爽'}</h1>
}
}
ReactDOM.render(<Weather/>, document.getElementById('test'))
// 自定义方法
function demo() {
// 所以在这个位置,根本就碰不到自定义类式组件中的组件实例对象。
// 因为Weather类中的实例对象,压根不是你new Weather()出来的,你怎么碰? 你只是写了<Weather/>,React帮你new的实例
// 怎么解决?
}
</script>
用一种笨方法解决:
<script type="text/babel">
let that
// 定义类组件
class Weather extends React.Component {
constructor(props) {
super(props)
this.state = {isHot: true}
that = this // 在构造器中,缓存this
}
render() {
const { isHot } = this.state
return <h1 onClick={demo}>今天天气很{isHot ? '炎热' : '凉爽'}</h1>
}
}
// 渲染类组件
ReactDOM.render(<Weather/>, document.getElementById('test'))
// 自定义方法
function demo() {
console.log(that) // 则这里的that肯定是构造器中的this,而构造器中的this又是组件实例对象。所以 that.state.isHot肯定可以取到值。
}
</script>
停!来观察下代码结构。在react中,希望只有上面两个:定义类组件和渲染类组件。其他相关的都放进定义类的内部。
<script type="text/babel">
// 定义类组件
class Weather extends React.Component {
constructor(props) {
super(props)
this.state = {isHot: true}
}
render() {
const { isHot } = this.state
return <h1 onClick={demo}>今天天气很{isHot ? '炎热' : '凉爽'}</h1>
}
// 自定义方法--注意,类不是函数体,在类中定义函数,不用加 function
// function demo() {
// console.log(that)
// }
demo() {
// demo()放在哪里了?--- Weather类的原型对象上。供实例使用
// 通过Weather的实例调用demo时,demo中的this就是Weather实例。
console.log(this.state.isHot)
}
}
// 渲染类组件
ReactDOM.render(<Weather/>, document.getElementById('test'))
// 报错了:demo is not defined
// 屡一下:onClick时会调用 demo方法, `onClick={demo}`。那么直接这么写就能够找到类中的demo方法吗?
// 上面说了,通过Weather的实例调用demo时,demo中的this就是Weather实例。所以必须通过this调用demo才可以访问到demo
// 所以必须这么写 `onClick={this.demo}`
</script>
<script type="text/babel">
// 定义类组件
class Weather extends React.Component {
constructor(props) {
super(props)
this.state = {isHot: true}
}
render() {
const { isHot } = this.state
// 通过Weather的实例调用demo时,demo中的this就是Weather实例。
return <h1 onClick={this.demo}>今天天气很{isHot ? '炎热' : '凉爽'}</h1>
}
demo() {
console.log(this.state.isHot)
}
}
// 渲染类组件
ReactDOM.render(<Weather/>, document.getElementById('test'))
// 点击,又报错:Cannot read property 'state' of undefined
// 疑问:为什么constructor、 render中都能够访问到this,或者说都能访问到组件实例。偏偏自定义的demo中就不能呢?
// 注意这句话:只有通过Weather的实例调用demo时,demo中的this才是Weather实例对象。
// 因为demo不是通过Weather实例调用的。所以导致this是undefined。对。这是这么回事。凭什么demo就不是实例调用的?onClick={this.demo}这种写法并不是通过实例调用的
</script>
回顾下js中的this丢失问题:
class Person {
constructor(name, age) {
this.name = name
this.age = age
}
speak() {
// speak放在哪里了?--Person类的原型对象上。供实例调用
console.log(this)
}
}
const p1 = new Person('tom', 18)
p1.speak() // 通过实例调用speak方法
const x = p1.speak // 注意这里:这里赋值语句
x() // undefined 这里x()是什么调用。注意,这属于直接调用。不是实例调用。
// 既然x()属于函数直接调用,那么this应该指向window。为什么是undefined?
// 因为类中定义的方法,开启了局部严格模式。所以说方法中的this没有指向window,而是undefined。
所以,回过头来看看
<script type="text/babel">
// 定义类组件
class Weather extends React.Component {
constructor(props) {
super(props)
this.state = {isHot: true}
}
render() {
const { isHot } = this.state
// 这里的 onClick={this.demo} 写完后,到底有没有调用demo?根本没有调用。
// 这里只是顺着原型链找到了demo方法,然后把这个函数直接交给onClick作为回调了。
// 当点击时,直接从堆里面把demo函数,直接调用。所以这里还算是通过实例调用吗?根本不是。
return <h1 onClick={this.demo}>今天天气很{isHot ? '炎热' : '凉爽'}</h1>
}
demo() {
// 由于demo是作为onClick的回调,所以不是通过实例调用的,是直接调用。
// 类中的方法默认开启了局部严格模式,所以demo中的this为undefined。
console.log(this.state.isHot)
}
}
// 渲染类组件
ReactDOM.render(<Weather/>, document.getElementById('test'))
</script>
bind(obj)会返回一个新的函数,这个新函数中的this指向obj。
我们想要实现:demo中的this指向Weather的实例对象。只有在demo中拿到了Weather的实例对象。才可以.state.isHot去修改它的值。
<script type="text/babel">
// 定义类组件
class Weather extends React.Component {
constructor(props) {
super(props)
this.state = {isHot: true}
// 一行代码解决 demo中this指向。【重点分析】。下面是赋值语句。
// 先分析右侧:this.demo.bind中的this是谁?----构造器中的this是组件实例对象。
// this.demo --- 组件实例对象自身有没有demo?---没有,demo放在了Weather的原型对象上。纵使组件实例对象自身没有demo方法,也会顺着原型找到原型上的demo方法。
// this.demo.bind 做了两件事情:第一返回一个新的函数,第二,帮你改了新函数中的this,改成了参数中的对象。这里改成了this,this是谁?----构造器中的this是组件实例对象
// 所以,= 右侧执行完之后的结果是:你得到了一个新函数,而且这个函数里的this已经成功的变为了Weather的实例对象。
// 分析 = 左侧: 然后,你把这个新函数放到了实例的自身,并且给这个函数起了一个名字,叫 demo.
this.demo = this.demo.bind(this)
}
render() {
const { isHot } = this.state
// 问题:当点击 h1时,调用的demo到底是原型链上的 demo还是挂在自身上的demo?
// 答案是: 自身上的 demo。 因为按照查找的顺序,自身已经有demo后就不会再去查找原型链。
return <h1 onClick={this.demo}>今天天气很{isHot ? '炎热' : '凉爽'}</h1>
}
demo() {
console.log(this.state.isHot)
}
}
// 渲染类组件
ReactDOM.render(<Weather/>, document.getElementById('test'))
</script>
需求是:获取原来isHot的值,修改
<script type="text/babel">
// 定义类组件
class Weather extends React.Component {
// 构造器调用几次?---- 1 次(new 了几次就调用几次)
constructor(props) {
console.log('constructor')
super(props)
this.state = {isHot: true, wind: '微风'}
this.demo = this.demo.bind(this)
}
// render 调用几次?----- 1 + n 次 1是初始化的那次, n 是状态更新的次数
render() {
console.log('render')
const { isHot, wind } = this.state
return <h1 onClick={this.demo}>今天天气很{isHot ? '炎热' : '凉爽'},{wind}</h1>
}
// demo 调用几次?----- 点几次调用几次
demo() {
console.log('demo')
// 获取原来的isHot
const isHot = this.state.isHot
// 严重注意:状态(state) 必须通过setState进行更新,且更新是一种合并,不是替换。
this.setState({
isHot: !isHot
})
// 严重注意:状态(state)不可直接更改,下面这行就是直接更改!!!
// this.state.isHot = !isHot // 这是错误的写法
}
}
// 渲染类组件
ReactDOM.render(<Weather/>, document.getElementById('test'))
</script>
总结:
构造器为什么要写? ------可以不写。写是因为需要在构造器中初始化状态、构造器还能解决this指向问题。
上面的代码很标准。但是在真实的开发中,上面的代码可能都不这么写。
我们来分析下代码:
<script type="text/babel">
// 定义类组件
class Weather extends React.Component {
constructor(props) {
super(props)
this.state = {isHot: true, wind: '微风'}
this.demo = this.demo.bind(this)
}
// 对于constructor中的this, 就是该Weather类的实例对象
render() {
const { isHot, wind } = this.state
return <h1 onClick={this.demo}>今天天气很{isHot ? '炎热' : '凉爽'},{wind}</h1>
}
// 对于render中的this,也是Weather类的实例对象。
// 为什么呢?是因为你用了 <Weather/>。React做了这么件事:const w1 = new Weather(),然后通过w1.render调用了render。所以render中的this也是实例对象。
demo() {
const isHot = this.state.isHot
this.setState({
isHot: !isHot
})
}
// 但是在demo中,this指向就出问题了。为什么?因为demo根本就不是w1.demo()调用的。而是作为事件的回调在调用。等你触发了事件之后,它直接把这个demo直接调用
// 而且类里面的方法开了严格模式,所以this丢了。
demo2() {}
demo3() {}
// 如果在类里面又写了自定义的demo2和demo3。你觉得这些方法都是怎么被调用的呢?----绝对不可能是w1.demo2()或者w1.demo3()调用。
// react不会在new Weather创建出w1实例的时候,再帮你调用w1.demo2()和w1.demo3()。只会调w1.render()。所以你在类中写的自定义方法,基本上都是给事件做回调
// 使用的。 demo2可能是失去焦点时调用,demo3可能是鼠标划过的时候调用。
// 而如果全部是作为事件的回调在使用,那么在demo2和demo3中的this都会是undefined。如果想解决这个问题,就需要在constructor中写
// `this.demo1 = this.demo1.bind(this)`、`this.demo2 = this.demo2.bind(this)`...
// 如果事件回调特别多的情况下,就需要在constructor中写大量的bind代码。
// 问个问题:当初为什么要写构造器?----
// 由于需要给实例对象进行一些初始化操作,所以需要在constructor中写 this.state = {...}。因为没地方写,所以只能借助构造器来做初始化的事情。
// 所以,构造器写的很被动。
}
// 渲染类组件
ReactDOM.render(<Weather/>, document.getElementById('test'))
</script>
先来复习下类中的小技巧
class Car {
constructor(name, price) {
this.name = name
this.price = price
// this.wheel = 4 // 想给每个实例都加一个共同的属性。可以在构造器中这么写。
}
// 类中可以直接写赋值语句,如下代码的含义是:给Car的实例对象添加一个属性,名为a,值为1
// a = 1
wheel = 4 // 但是如果值是new时传递过来的话,就还得需要在constructor中接
}
const c1 = new Car('奔驰C63', 199)
const c2 = new Car('宝马', 299)
console.log(c1)
console.log(c2)
// c1和c2上就都有一个wheel属性,值是4
回过头来看
<script type="text/babel">
// 定义类组件
class Weather extends React.Component {
constructor(props) {
super(props)
// 同学,我是不是想把所有Weather实例对象身上都追加一个state属性,然后它的值是一个对象。
// 所以下面这行代码可以不写在构造器中,直接拎到外面
// this.state = {isHot: true, wind: '微风'}
// 下面这行代码怎么简化?
// this.demo = this.demo.bind(this)
}
// 往Weather的所有实例自身追加一个state属性,值是一个对象。
state = {isHot: true, wind: '微风'}
render() {
const { isHot, wind } = this.state
return <h1 onClick={this.demo}>今天天气很{isHot ? '炎热' : '凉爽'},{wind}</h1>
}
// demo放在了哪里?----Weather类的原型对象上。
// demo() {
// const isHot = this.state.isHot
// this.setState({
// isHot: !isHot
// })
// }
// 是不是赋值语句? ----是
// 这样写:demo放在了哪里? ----放在了实例自身上。Weather的原型上已经没有了 demo
// demo = function() {
// const isHot = this.state.isHot
// this.setState({
// isHot: !isHot
// })
// }
// 这么写仅仅是把demo换了一个地方,原来demo在Weather的原型对象上,现在在Weather所缔造的实例对象自身。但这么写并没有解决this指向问题
// 紧接着,我们把普通函数换成箭头函数
demo = () => {
console.log(this) // 确定:这里的this 就是组件实例对象
const isHot = this.state.isHot
this.setState({
isHot: !isHot
})
}
// 箭头函数有一大特点:没有自己的this。如果在箭头函数中使用了this,它不会报错,而是使用了箭头函数外层的this作为自己的this。
}
// 渲染类组件
ReactDOM.render(<Weather/>, document.getElementById('test'))
</script>
所以精简之后的代码是:
<script type="text/babel">
// 定义类组件
class Weather extends React.Component {
// 初始化状态
state = {isHot: true, wind: '微风'}
render() {
const { isHot, wind } = this.state
return <h1 onClick={this.demo}>今天天气很{isHot ? '炎热' : '凉爽'},{wind}</h1>
}
// 自定义方法 ----- 要用赋值语句 + 箭头函数
// 以后在类中写方法--都这么写了
demo = () => {
const isHot = this.state.isHot
this.setState({
isHot: !isHot
})
}
}
// 渲染类组件
ReactDOM.render(<Weather/>, document.getElementById('test'))
</script>
- state是组件对象最重要的属性,值是对象(可以包含多个key-value的组合)
- 组件被称为“状态机”,通过更新组件的state来更新对应的页面显示(重新渲染组件)
强烈注意:
-
组件中render方法中的this为组件实例对象
-
组件自定义的方法中 this 为 undefined,如何解决?
a. 强制绑定 this: 通过函数对象的 bind()
b. 赋值语句 + 箭头函数
-
状态数据,不能直接修改或更新