心血来潮逛了一遍react官网文档,按照文档目录对之前没有深入了解的知识点进行了归纳,可以配合react官网文档进行阅读
react渲染执行顺序
useEffect
- 执行时机:React 完成 DOM 更新后且浏览器绘制结束后,异步调用。不阻塞渲染
- 执行顺序:
- 挂载时会执行一次副作用(传入的函数)(注:开发环境严格模式下会先执行一次副作用与清理函数的循环(做一次检测))
- 更新时会先执行上一次状态的清理函数,再以最新状态执行副作用
- 用途:适用于不需要阻塞浏览器绘制的副作用,如网络请求、日志记录、设置订阅等。
useLayoutEffect
- 执行时机:在 React 完成 DOM 更新后浏览器绘制之前,同步调用。阻塞渲染
- 用途:适用于需要读取 DOM 布局并同步重渲染的副作用,如测量 DOM 元素尺寸、同步布局等。因为它会在浏览器有机会绘制之前执行,所以可能会影响性能。
打印顺序:1,2,6,10,9,8,7,5,3,4
- 父函数作用于->子函数作用域->子effect->父effect
- effect 按照定义顺序执行
import "./styles.css";
import { memo, useCallback, useEffect, useMemo } from "react";
import React, { useState } from "react";
import { Button, Spin } from "antd";
// 父组件
const App = () => {
console.log(1);
const [loading, setLoading] = useState(false);
const [count, setCount] = useState(0);
const conutObj = useMemo(() => {
a: 1;
}, []);
console.log(2);
useEffect(() => {
console.log(5);
}, []);
useEffect(() => {
console.log(3);
});
useEffect(() => {
console.log(4);
});
const handleClick = useCallback(() => {
console.log(count);
setCount((pre) => pre + 1);
console.log("click");
}, []);
const handleClickLoad = () => {
setLoading(!loading);
};
console.log("父渲染了");
return (
<div>
{loading ? "loading" : "load"}
<Button onClick={handleClickLoad}>修改loading</Button>
<Button onClick={handleClick}>修改count</Button>
<Child
onClick={handleClick}
count={count}
setCount={setCount}
conutObj={conutObj}
// setConutObj={setConutObj}
/>
</div>
);
};
// 子组件
const Child = memo((props) => {
console.log(6);
useEffect(() => {
console.log(9);
});
useEffect(() => {
console.log(8);
}, []);
useEffect(() => {
console.log(7);
}, [props.count]);
console.log(10);
// console.log(props.count);
console.log("子组建渲染了");
return <Button onClick={props.onClick}>child</Button>;
});
export default App;
state 如同一张快照
React 会使 state 的值始终“固定”在一次渲染的各个事件处理函数内部
import { useState } from 'react';
export default function Counter() {
const [number, setNumber] = useState(0);
return (
<>
<h1>{number}</h1>
<button onClick={() => {
setNumber(number + 5);
setTimeout(() => {
alert(number);
}, 3000);
}}>+5</button>
</>
)
}
以上例子中,即使加了5s的定时器,但是每次渲染的值都是相同的,即0,5,10
把一系列 state 更新加入队列
想在重新渲染之前读取最新的state,可以传入纯函数,参数为最新的state值
import { useState } from 'react';
export default function Counter() {
const [number, setNumber] = useState(0);
return (
<>
<h1>{number}</h1>
<button onClick={() => {
setNumber(number + 5);
setNumber(n => n + 1);
}}>增加数字</button>
</>
)
}
在下一次渲染期间,React 会遍历 state 队列:
更新队列 | n | 返回值 |
---|---|---|
“替换为 5 ” | 0 (未使用) | 5 |
n => n + 1 | 5 | 5 + 1 = 6 |
React 会保存 6
为最终结果并从 useState
中返回。
更新state中的数组
当你操作 React state 中的数组时,你需要避免使用(改变数组)的左列的方法,而首选右列的(返回新数组)方法
| 避免使用 (会改变原始数组) | 推荐使用 (会返回一个新数组) |
| --------------- | -------------------------- | ----------------------------------------------------------------------------------------------------------- |
| 添加元素 | push
,unshift
| concat
,[...arr]
展开语法(例子) |
| 删除元素 | pop
,shift
,splice
| filter
,slice
(例子) |
| 替换元素 | splice
,arr[i] = ...
赋值 | map
(例子) |
| 排序 | reverse
,sort
| 先将数组复制一份(例子)
受控组件和非受控组件
当编写一个组件时,你应该考虑哪些信息应该受控制(通过 props
控制),哪些信息不应该受控制(通过自身 state
控制)。当然,你可以随时改变主意并重构代码。
对 state 进行保留和重置
- 只要在相同位置渲染的是相同组件, React 就会保留状态。
// 因为渲染在ui树的同个位置,即使切换组建状态也会被保留
import { useState } from 'react';
export default function App() {
const [isFancy, setIsFancy] = useState(false);
return (
<div>
{isFancy ? (
<Counter isFancy={true} />
) : (
<Counter isFancy={false} />
)}
<label>
<input
type="checkbox"
checked={isFancy}
onChange={e => {
setIsFancy(e.target.checked)
}}
/>
使用好看的样式
</label>
</div>
);
}
- state 不会被保存在 JSX 标签里。它与你在树中放置该 JSX 的位置相关联。
- 你可以通过为一个子树指定一个不同的 key 来重置它的 state。
- 不要嵌套组件的定义,否则你会意外地导致 state 被重置。
flushSync 立即更新dom
要强制 React 同步更新(“刷新”)DOM。 为此,从 react-dom
导入 flushSync
并将 state 更新包裹 到 flushSync
调用中:
import { flushSync } from 'react-dom';
flushSync(() => {
// 执行完立刻更新dom
setTodos([ ...todos, newTodo]);
});
// 能滚动到变化后最新的dom上
listRef.current.lastChild.scrollIntoView();
这将指示 React 当封装在 flushSync
中的代码执行后,立即同步更新 DOM。因此,当你尝试滚动到最后一个待办事项时,它已经在 DOM 中了
useEffectEvent - react18新hook
useEffectEvent
这个 React Hook 让你可以提取非响应式逻辑到 Effect Event 中。
- 可以在useEffect 中执行响应式的逻辑且不需要监听这个响应式的变量
- 声明后必须在useEffect中调用这个方法
const onSomething = useEffectEvent(callback)
最后:如果这篇文章如果帮到你了,帮忙点个赞,或者关注哈!!!