day-02
组件的状态
关于状态
概念
状态就是用来描述事物在某一时刻的的数据,例如:9 月 23 号时书的库存数量;18 岁时人的身高等。
特点
状态能被改变,改变了之后视图会有对应的变化。
作用
- 保存数据。
- 数据变化响应到视图(React 包内部的操作)。
有状态/无状态组件
- 有状态组件:能定义 state 的组件,类组件就是有状态组件。
- 无状态组件:不能定义 state 的组件,函数组件一般叫做无状态组件。
🧐 2019 年 02 月 06 日,React v16.8 中引入了 React Hooks,从而函数式组件也能定义自己的状态了。
无状态组件的应用场景
- 组件本身就不需要有状态,例如渲染一段静态的内容。
- 组件本身就没有状态,有可能只需要从外部传入的状态就够了。
类组件的状态
目标
掌握 React 类组件中如何定义和使用状态。
定义
第一种方式:在 constructor 中通过 this.state = {} 。
class App extends React.Component {
constructor() {
super()
this.state = {
list: [
{ id: 1, name: '明天会更好' },
{ id: 2, name: '今天' },
],
}
}
}
第二种方式:通过 state 来定义状态,state 对应的值必须是一个对象。
class App extends React.Component {
state = {
list: [
{ id: 1, name: '明天会更好' },
{ id: 2, name: '今天' },
],
}
}
🧐 思考两种方式的差异?
两种定义状态的差异:
ocnstructor中定义,new的时候可以传参,传递的参数可以当做状态
而state={ }不方便传参
事件绑定
目标
- 掌握 React 中如何进行事件绑定。
- 掌握 React 中如何获取事件对象。
语法
<元素 事件名1={事件处理函数1} 事件名2={事件处理函数2}></元素>
注意:React 事件名采用驼峰命名法,比如 onClick、onMouseEnter 等。
类组件中事件绑定
需求:点击按钮控制台打印 ‘Hello World’。
class App extends React.Component {
handleClick() {
console.log('Hello World')
}
render() {
return <button onClick={this.handleClick}>click</button>
}
}
函数式组件中的事件绑定
const App = () => {
const handleClick = () => {
console.log('Hello World')
}
return <button onClick={handleClick}>click</button>
}
获得事件对象
通过形参 e 可以拿到事件对象,例如 e.target 就是触发事件的那个 DOM 元素。
小结
-
事件命名的规则是什么? 驼峰命名法 -
如何拿到事件对象? e.target
解决 this 指向问题
直接调用类中的方法,this指向的是undefined
高阶函数:返回函数的函数,函数作为参数传递的函数 例如:arr.map(()=>{})
柯里化:通过函数调用继续返回函数的形式,实现多次接收参数最后统一处理的函数编码形式。
方法 1
高阶函数:通过 this 来直接调用 handleClick 并返回箭头函数。
🧐 柯里化:通过函数调用继续返回函数的形式,实现多次接收参数最后统一处理的函数编码形式。
class App extends React.Component {
state = {
count: 0,
}
handleClick() {
return () => {
console.log(this.state.count)
}
}
render() {
return (
<div>
<h2>计数器:{this.state.count}</h2>
<button onClick={this.handleClick()}>+1</button>
</div>
)
}
}
方法 2
包裹一层箭头函数。
箭头函数中的 this 指向“外部”,即 render 函数,而 render 函数中的 this 正是组件实例。
class App extends Component {
state = {
count: 0,
}
handleClick() {
console.log(this.state.count)
}
render() {
return (
<div>
<h2>计数器:{this.state.count}</h2>
<button onClick={() => this.handleClick()}>+1</button>
</div>
)
}
}
方法 3
使用 bind。
bind谁,handleClick中的this就是谁
class App extends Component {
state = {
count: 0,
}
handleClick() {
console.log(this.state.count)
}
render() {
return (
<div>
<h2>计数器:{this.state.count}</h2>
<button onClick={this.handleClick.bind(this)}>+1</button>
</div>
)
}
}
扩展
es6 语法:
🤔 关于 class 中的实例方法和原型方法?
原型方法演示
class App {
handleClick() {}
}
const app1 = new App()
const app2 = new App()
console.log(app1)
console.log(app1.handleClick === app2.handleClick)
实例方法演示
class App {
handleClick = () => {}
}
const app1 = new App()
const app2 = new App()
console.log(app1)
console.log(app1.handleClick === app2.handleClick)
所以,要明白在 class 中直接写的方法和通过赋值语句添加的方法本质上不一样。
在 class 中直接写的方法是原型方法。
在 class 中通过赋值语句添加的方法是实例方法。
es5语法 :
注意:在 constructor 中挂载的方法也是实例方法。
注意:语法规范:继承的时候,子组件一旦有constructor,constructor内部必须调用super()
静态属性
通过:static 挂载
class App{
static age=18
}
App.gender='男'
console.log(App.age)
原型链:
多个对象之间通过 proto 链接起来的这种关系就是原型链
a ==> App ==> Object.prototype
方法 4
通过赋值语句往实例上面添加一个箭头函数。
class App extends Component {
state = {
count: 0,
}
handleClick = () => {
console.log(this.state.count)
}
render() {
return (
<div>
<h2>计数器:{this.state.count}</h2>
<button onClick={this.handleClick}>+1</button>
</div>
)
}
}
证明“外层” this 确实是组件实例
class App {
temp = this
}
const app = new App()
console.log(app === app.temp)
方法 5
在构造函数中再创建一个实例方法,和原型方法公用一个函数体。
class App extends React.Component {
constructor() {
super()
this.state = {
count: 0,
}
this.handleClick = this.handleClick.bind(this)
}
handleClick() {
console.log(this.state.count)
}
render() {
return (
<div>
<h2>计数器:{this.state.count}</h2>
<button onClick={this.handleClick}>+1</button>
</div>
)
}
}
修改状态
不要直接修改原数据
目标
掌握通过 setState 修改状态的写法。
错误写法
this.state.count += 1 // 数据确实也会变,但不是响应式的!
内容
- 语法:
this.setState({ 要修改的部分数据 }) 。 - 作用:修改 state 并更新视图。
- 来源:
setState() 函数是通过继承而来的。 - 注意:
setState() 的操作是合并,不会影响没有操作到的数据。
this.setState({ count: this.state.count + 1 })
小结
通过哪个方法来修改 state 中的数据?
this.setState( )
状态的不可变性
目标
了解 React 的核心理念,状态的不可变性。
解释
也就是说不要直接修改原数据,而是要产生一份新数据,然后通过 setState() 用新的数据覆盖原数据,这么做的其中一个重要原因就是为了 SCU(shouldComponentUpdate),为了性能优化。
受控表单组件
目标
能够使用受控组件的方式收集到表单中的数据。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-23cEQY53-1638796961376)(react.assets/ifer_form.png)]
概念
受控不受控一般是针对表单来说的,所谓受控表单组件,即表单元素的 value 值受到了 React 中状态(state)的控制(对状态的操作会影响视图,视图的变化又会反映到状态上)。
input
- 在 state 中添加一个状态,作为表单元素的 value 值(数据影响视图)。
- 给表单元素绑定 onChange 事件,将表单元素的值设置为 state 的值(视图影响数据)。
textarea
操作方式和 input 框一样。
select
radio
多个单选按钮,绑定的值可以是一个字符串。
checkbox
绑定的值可以是一个数组。
import React, { Component } from 'react'
export default class App extends Component {
state = {
userName: 'zs',
info: 'qweqwewq',
frame: 'vue',
gender: 'male',
checkbox: ['orange'],
}
handelChange = (e) => {
console.log(e.target.type)
if (e.target.type !== 'checkbox') {
this.setState({
[e.target.name]: e.target.value,
})
} else {
if (e.target.checked) {
this.setState({
[e.target.name]: [...this.state.checkbox, e.target.value],
})
} else {
const tempCheckbox = [...this.state.checkbox].filter((item) => {
return item !== e.target.value
})
this.setState({
[e.target.name]: tempCheckbox,
})
}
}
}
render() {
const { userName, info, frame, gender, checkbox } = this.state
return (
<ul>
<li>
<label htmlFor="userName">用户名</label>
<input
type="text"
id="userName"
name="userName"
value={userName}
onChange={this.handelChange}
></input>
</li>
<li>
<label htmlFor="info">其他信息</label>
<textarea
id="info"
name="info"
value={info}
onChange={this.handelChange}
cols="30"
rows="10"
></textarea>
</li>
<li>
<label htmlFor="frame">框架</label>
<select
id="frame"
name="frame"
value={frame}
onChange={this.handelChange}
>
<option value="react">react</option>
<option value="vue">vue</option>
<option value="Angular">Angular</option>
</select>
</li>
<li>
<label>
男:
<input
type="radio"
value="male"
name="gender"
checked={gender === 'male'}
onChange={this.handelChange}
></input>
</label>
<label>
女:
<input
type="radio"
value="female"
name="gender"
checked={gender === 'female'}
onChange={this.handelChange}
></input>
</label>
<label>
未知:
<input
type="radio"
value="unknown"
name="gender"
checked={gender === 'unknown'}
onChange={this.handelChange}
></input>
</label>
</li>
<li>
<label>
Apple
<input
type="checkbox"
value="Apple"
name="checkbox"
checked={checkbox.includes('Apple')}
onChange={this.handelChange}
></input>
</label>
<label>
orange
<input
type="checkbox"
value="orange"
name="checkbox"
checked={checkbox.includes('orange')}
onChange={this.handelChange}
></input>
</label>
</li>
</ul>
)
}
}
非受控表单组件
概念
非受控组件则是通过操作 DOM 的方式来获取数据,表单中的 value 并没有和 state 中的数据进行绑定。
内容
通过 React.createRef() 获取 DOM。
import React, { Component } from 'react'
export default class App extends Component {
input = React.createRef()
handleChange = () => {
console.log(this.input.current.value)
}
render() {
return (
<div>
{}
<input ref={this.input} type='text' placeholder='输入内容' onChange={this.handleChange} />
</div>
)
}
}
|