9.6 不可变数据的力量
我们知道是不能够修改this.state里面的数据的
举个例子
export class App extends React.PureComponent{
constructor(){
super();
this.state = {
userList:[
{name : "tom" , age : 18},
{name : "lily" , age : 20},
{name : "tiki" , age : 18},
]
}
}
render(){
const userList =this.state.userList
return (
<div>
<ul>
{
userList.map((item , index , arr) => {
return (
<li key={index}>{item.name} + {item.age}</li>
)
})
}
</ul>
<button onClick={() => this.addNewUser()}>点击添加</button>
</div>
)
}
不推荐的做法:
const newData = {name : "hcy" , age : 19}
this.state.userList.push(newData);
this.setState({
userList:this.state.userList
})
因为我们修改了state里面的数据,但是这种方式直接修改原数组,不符合 React 的不可变性原则
我们的APP继承了PureComponent,它有shouldcomponnets方法,这个方法必须发生改变才会执行,但是我们这样的方法指向同一个地址(相同)所以是不会改变的
**推荐做法:**不要直接修改 state,应该用新数组。
const newData = {name : "hcy" , age : 19}
this.setState({
userList:[...this.state.userList , newData]//不要直接修改 state,应该用新数组。
})
9.7 全局事件传递events
开发中跨组件之间的事件传递,使用events
通过npm或者yarn来安装events
npm add events
events常用的API:
- 创建EventEmitter对象:eventBus对象;
- 发出事件:eventBus.emit("事件名称", 参数列表);
- 监听事件:eventBus.addListener("事件名称", 监听函数);
- 移除事件:eventBus.removeListener("事件名称", 监听函数);
事件演练:
import React, { PureComponent } from 'react'
import {EventEmitter} from 'events'
// 需求:点击profile中的按钮->进行跨组件之间的事件传递
// 1、创建EventEmitter对象:eventBus对象
const evebtBus = new EventEmitter();
export default class App extends PureComponent {
render() {
return (
<div>
<Header />
<Profile/>
</div>
)
}
}
class Header extends PureComponent{
// 进行监听
componentDidMount(){
// 添加事件监听
// 3、监听事件:eventBus.addListener("事件名称", 监听函数);
// (eventName: string | symbol, listener: (...args: any[]) => void):
// this.handleSayHelloListener:没有括号,因为是函数
evebtBus.addListener("sayHello" , this.handleSayHelloListener)
}
componentWillUnmount(){
// 取消事件监听
// (...args: any[]) => void)
// 4、 移除事件:eventBus.removeListener("事件名称", 监听函数);
evebtBus.removeListener("sayHello" ,this.handleSayHelloListener)
}
// ...args
// 多个参数要写多个形参
handleSayHelloListener(str , num){
console.log(str + num);
}
render (){
return (
<div>
Hello World
</div>
)
}
}
class Profile extends PureComponent{
render (){
return(
<div>
Hello Profile
<button onClick={() => this.emmitEvent()}>点击Profile</button>
</div>
)
}
// 2、发出事件:eventBus.emit("事件名称", 参数列表);
emmitEvent(){
evebtBus.emit("sayHello" , "Hello Home" , 123)
}
}
10、受控和非受控组件
10.1 refs的使用
- 方式一:传入一个对象
-
- 对象是通过 React.createRef() 方式创建出来的;
- 使用时获取到创建的对象其中有一个current属性就是对应的元素;
- 方式二:传入一个函数
-
-
该函数会在DOM被挂载时进行回调,这个函数会传入一个 元素对象,我们可以自己保存;
-
使用时,直接拿到之前保存的元素对象即可;
import React, { PureComponent } from 'react';
import Pure from './Pure';
export default class App extends PureComponent {
constructor() {
super();
this.titleRef = React.createRef();
this.titleEle = null;
}render() {
return (
{/* 1、React.createRef() /}
Hello World
{/ 2、传入一个函数 */}
<h2 ref={(args) => this.titleEle = args}>Hello World
<button onClick={() => this.changeRef()}>点击切换</button>
);
}changeRef() {
this.titleRef.current.innerHTML = "Hello React";
this.titleEle.innerHTML = "Hello React"
}
appAdd(){
console.log(this.pureRef.current.btnAdd());
}
}
-
10.2 ref的类型
ref 的值根据节点的类型而有所不同:
- 当 ref 属性用于 HTML 元素时,构造函数中使用 React.createRef() 创建的 ref 接收底层 DOM 元素作为其 current 属性;
- 当 ref 属性用于自定义 class 组件时,ref 对象接收组件的挂载实例作为其 current 属性;
- 你不能在函数组件上使用 ref 属性,因为他们没有实例;
举一个组件上使用ref的例子:
// 组件的ref
this.pureRef = React.createRef();
{/* 3、当 ref 属性用于自定义 class 组件时,ref 对象接收组件的挂载实例作为其 current 属性; */}
<Pure ref={this.pureRef}/>
<button onClick={() => this.appAdd()}>app+1</button>
10.3 受控组件
受控组件是单元素(如<input>
、<textarea>
等)的值由React的state进行"控制",即组件的状态(state)是唯一数据源。
import React, { PureComponent } from 'react'
export default class App extends PureComponent {
constructor(){
super();
this.state = {
username:""
}
}
render() {
return (
<div>
<form>
<input
type='text'
value={this.state.username}
onChange={(e) => this.handleChange(e)}
/>
<input
type='submit'
onClick={(e) => this.handleSubmit(e)}
/>
</form>
</div>
)
}
handleSubmit(event){
event.preventDefault();
console.log("提交内容:",this.state.username)
}
handleChange(event){
this.setState({
username:event.target.value
})
}
}
<input>
的 value
属性绑定了 this.state.username
,输入框的内容只能通过 setState
来改变。每当用户输入内容时,onChange
事件会触发 handleChange
,进而更新 state,React 再把最新的 state 赋值给 input 的 value。
总结:
受控组件的输入值受 React 组件的 state 控制,React 负责管理和同步表单数据,这就是"受控"的含义。
其他受控通过组件:比如select
import React, { PureComponent } from 'react'
export default class App extends PureComponent {
constructor(){
super();
this.state = {
fruit:"apple"//默认为苹果
}
}
render() {
return (
<div>
<form onSubmit={(e) => this.handleSubmit(e)}>
{/* 选择器 */}
<select
value={this.state.fruit}
name='fruits'
onChange={(e) => this.handleChange(e)}>
<option value="apple">苹果</option>
<option value="oringe">橘子</option>
<option value="bannana">香蕉</option>
</select>
</form>
</div>
)
}
handleSubmit(event){
event.preventDefault();
console.log("提交内容:",this.state.fruit)
}
handleChange(event){
this.setState({
fruit:event.target.value
})
}
}
受控组件---多输入
要是不抽取代码就会很冗余
import React, { PureComponent } from 'react'
export default class App extends PureComponent {
constructor(){
super();
this.state = {
username:"",
password:""
}
}
render() {
return (
<div>
<form>
<input
type='text'
name='username'
value={this.state.username}
onChange={(e) => this.handleChange(e)}
/>
<input
name='password'
type='password'
value={this.state.password}
onChange={(e) => this.handleChange(e)}
/>
<input
type='submit'
onClick={(e) => this.handleSubmit(e)}
/>
</form>
</div>
)
}
handleSubmit(event){
event.preventDefault();
const {username , password } = this.state;
console.log("提交内容:",username , password )
}
// handleusernameChange(event){
// this.setState({
// username:event.target.value
// })
// }
// handlepasswordChange(event){
// this.setState({
// password:event.target.value
// })
// }
handleChange(event){
this.setState({
[event.target.name] : event.target.value
})
}
// 我们可以把handleChange写为一个,因为她们的样式是都是:
// handle{key}Change(event){
// this.setState({
// {key}:event.target.value
// })
// }
// 我们只需要动态更新key就好
// 1、input设置name
// 2、event.target.name得到不同的key
}
10.4 非受控组件的使用
如果要使用非受控组件中的数据,那么我们需要使用 ref 来从DOM节点中获取表单数据。
在非受控组件中通常使用defaultValue来设置默认值;
import React, { createRef, PureComponent } from 'react'
export default class App extends PureComponent {
constructor(){
super();
this.nameRef = createRef()//创建ref对象
}
render() {
return (
<div>
<form>
<input
type='text'
ref={this.nameRef}//绑定ref
/>
<input
type='submit'
onClick={ this.handleSubmit}
/>
</form>
</div>
)
}
handleSubmit = (event) => {
event.preventDefault();
console.log("提交内容:",this.nameRef.current.value)
}//箭头函数
}