响应事件
- 创建事件处理函数
处理函数名常以handle+事件名
命名
function handlePlayClick() {alert(`Playing`);}
- 传递事件处理函数
函数名、匿名两种方式!
function PlayButton() {function handlePlayClick() {alert(`Playing`);}return (<Button handleClick={handlePlayClick}>Play "{movieName}"</Button>);
}function UploadButton() {return (<Button handleClick={() => alert('Uploading!')}>Upload Image</Button>);
}
- 接受事件处理函数
传参、匿名两种方式(⚠️传递的是函数名handlePlayClick
并非handlePlayClick()
!后者会在组件渲染时执行!) onClick
:事件名,仅支持浏览器事件名称。
function Button({ handleClick}) {function handlePlayClick() {alert(`Playing`);}return (<div><button onClick={handlePlayClick}></button><button onClick={handleClick}></button>// 匿名<button onClick={() => alert('Uploading!')}></button></div>);
}
事件传播
- 事件“冒泡”
单击3号button,会触发自身的onClick
事件,接着事件会冒泡上传;
2号button没有满足当前事件的函数,不执行,事件继续冒泡上传;
1号div存在满足当前事件的函数,触发1号div响应的函数,事件继续冒泡上传…
<div id={1} className="Toolbar" onClick={() => {alert('You clicked on the toolbar!');}}><button id={2} onDoubleClick={() => alert('Playing!')}><button id={3} onClick={() => alert('Uploading!')}>Uploading</button></button></div>
- 阻止事件传播:
e.stopPropagation()
事件处理函数接收事件对象e(代表 “event”)作为唯一的参数。
在事件名称末尾添加Capture
获取被捕获的事件,eg:onClickCapture
<div id={1} className="Toolbar" onClick={e => {e.stopPropagation();alert('You clicked on the toolbar!');}}>......
</div>
e.preventDefault()
: 阻止了少数事件的默认浏览器行为
例如在表单提交事件中,浏览器默认会刷新整个页面,e.preventDefault()
可以阻止这一行为。
<form onSubmit={e => {e.preventDefault();alert('Submitting!');}}>......</form>
Hooks :以 use
开头的函数
⚠️只能在组件或自定义的钩子函数的顶层被调用。 不能在条件、循环或其他嵌套函数中调用。
状态:useState
- 导入
import { useState } from 'react';
- 使用
0是默认值
something
是状态变量,setSomething
是设置函数。(均为自定义)
const [something, setSomething] = useState(0);
纯粹原则表明应视something
为只读,应通过setSomething
函数创建一个新的值去更新!
1. something
是一个对象时
- 更新
person
对象部分属性
function handleChange(e) {setPerson({firstName: e.target.value, // New first name from the inputlastName: person.lastName,email: person.email});}
- 展开对象写法
function handleChange(e) {setPerson({...person, // Copy the old fieldsfirstName: e.target.value // But override this one});}
- 动态
[
变量名]
写法
function handleChange(e) {setPerson({...person,[e.target.name]: e.target.value});}
- 当要修改的对象属性名被嵌套太深,可以使用Immer三方库
import React from "react";
import { useImmer } from "use-immer";function App() {......function updateName(name) {updatePerson(draft => {draft.name = name;});}......
}
2. something
是一个数组时
- 添加元素
setArtists([...artists,{ id: nextId++, name: name } // Put new item at the end
]);
或
setArtists([{ id: nextId++, name: name }, // Put new item at the head...artists
]);
- 删除元素
setArtists(artists.filter(a => a.id !== artist.id)
);
- 使用返回新数组的方法(a:元素,i:下标)
map()
:遍历每个元素
filter()
:返还条件为true的元素
artists.map(a => <li>a/<li>)
artists.map((a,i) => <li key={i}>a/<li>)artists.filter(a => a.id !== artist.id)
artists.filter((a,i) => i<10)
- 需要使用原地操作数组的方法时,使用
slice()
方法拷贝数组后,操作新数组。
⚠️这种拷贝都是浅拷贝(新数组中的对象仍然是对原始对象的引用)
/ 拷贝
let citrus = [...fruits] / 拷贝
let citrus = fruits.slice(); / 全部拷贝
let citrus = fruits.slice(1, 3); / 拷贝fruits[1,3)
/ 操作
const item = citrus.find(a => a.id === 2);
citrus.reverse();
citrus.sort();
/ 更新
setFruits(citrus);
- 当然,当数组元素为对象时,仍要创建一个新的值去更新
setMyList(myList.map(artwork => {return { ...artwork, seen: nextSeen };}));
- 使用Immer三方库简化数组嵌套
状态与渲染
- ⚠️React将
something
状态值存储在组件之外(组件存储的只是状态值的快照),每次setSomething()
更改的是组件外的状态值,当组件“稳定”(没有代码在执行)后,React会更改DOM从而触发组件渲染,通过渲染将更新后的状态值刷新到页面!!!
- 代码中
number
默认为0
当点击🔘+5时,alert(number)
的值仍然是0!
import { useState } from 'react';export default function Counter() {const [number, setNumber] = useState(0);return (<><h1>{number}</h1><button onClick={() => {setNumber(number + 5);alert(number);}}>+5</button></>)
}
- 那如果需要多次修改状态值时,怎么办?传入函数(参数名通常为状态值首字母)
setNumber(n => n + 1);
setNumber(n => n + 1);
setNumber(n => n + 1);
setEnabled(e => !e);
setLastName(ln => ln.reverse());
setFriendCount(fc => fc * 2);