react文档阅读

This commit is contained in:
2023-05-29 23:58:51 +08:00
parent bb73633973
commit 4ce0974356

View File

@@ -373,6 +373,147 @@ function handleNameChange(e) {
## 更新state数组
和object一样在react中数组应该也要是不可变的在更新state中的值时也应该再创建一个新的数组并用新的数组值设置
## 重新渲染时组件状态的保存
只要一个组件还在**UI树中相同的位置**那么react就会保存其state如果组件被移除或另有一个类型的组件被渲染在相同的位置那么react将会丢弃旧组件的state。
如果不同同类型组件被渲染在相同位置state仍然会被保存。
### 在UI树结构的同一位置渲染不同组件
如果在两次渲染之间在UI树的同一位置渲染了相同类型的组件那么默认情况下两次ui组件的state相同的。
如果要显式指定两次渲染的组件是不同的各自有独立的state可以为两次渲染的组件指定不同的key
```js
import { useState } from 'react';
export default function Scoreboard() {
const [isPlayerA, setIsPlayerA] = useState(true);
return (
<div>
{isPlayerA ? (
<Counter person="Taylor" />
) : (
<Counter person="Sarah" />
)}
<button onClick={() => {
setIsPlayerA(!isPlayerA);
}}>
下一位玩家!
</button>
</div>
);
}
function Counter({ person }) {
const [score, setScore] = useState(0);
const [hover, setHover] = useState(false);
let className = 'counter';
if (hover) {
className += ' hover';
}
return (
<div
className={className}
onPointerEnter={() => setHover(true)}
onPointerLeave={() => setHover(false)}
>
<h1>{person} 的分数:{score}</h1>
<button onClick={() => setScore(score + 1)}>
加一
</button>
</div>
);
}
```
> 注意key并不需要全局唯一key只用于指定父组件内部的顺序
### 前后两次组件渲染位于不同UI树位置
如果前后两次组件渲染时位于不同的ui树位置如果要保持前后两次渲染组件state相同可以为前后两次渲染指定相同的key那么在前后两次渲染时即使ui树位置不同state仍然会延续相同。
state和树中位置相关例如
```js
// 第一次渲染
return (
<ul>
<li key={id1}><Item key="a" /></li>
<li key={id2}><Item key="b"/></li>
</ul>
)
// 第二次渲染
return (
<ul>
<li key={id2}><Item key="b"/></li>
<li key={id1}><Item key="a"/></li>
</ul>
)
```
在上述两次渲染中,`item a`和`item b`父节点`<li>`的顺序虽然发生了改变,但是能够通过`<li>`元素的key属性来区分故而`item a`和`item b`的state仍然会保持原样。显示效果为列表第一行元素和第二行元素发生了对调。
## Reducer
reducer是一个函数接受两个参数
- 当前state
- action代表改变状态的参动作数据结构可以自定义
reducer方法的返回值是一个新的statereact会将旧的状态设置为新的状态。
### Reducer使用
```js
import { useReducer } from 'react';
// 用于替换useState
// useReducer接受两个参数一个reducer函数一个state的初始值
// 其会返回有状态的值和一个dispatch函数dispatch函数用于分发action给reducer
const [tasks, dispatch] = useReducer(tasksReducer, initialTasks);
// 之后可以向dispatch函数提交action
function handleAddTask(text) {
dispatch({
type: 'added',
id: nextId++,
text: text,
});
}
function handleChangeTask(task) {
dispatch({
type: 'changed',
task: task,
});
}
// reducer函数会处理action
function tasksReducer(tasks, action) {
switch (action.type) {
case 'added': {
return [
...tasks,
{
id: action.id,
text: action.text,
done: false,
},
];
}
case 'changed': {
return tasks.map((t) => {
if (t.id === action.task.id) {
return action.task;
} else {
return t;
}
});
}
case 'deleted': {
return tasks.filter((t) => t.id !== action.id);
}
default: {
throw Error('未知 action: ' + action.type);
}
}
}
```