react文档阅读
This commit is contained in:
141
react/react文档.md
141
react/react文档.md
@@ -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方法的返回值是一个新的state,react会将旧的状态设置为新的状态。
|
||||
|
||||
### 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user