监听普通变量
- 点击按钮修改变量n
- useEffect是监听不到的
- 因为修改变量n没有触发重新渲染
import { useEffect, useState } from 'react'
let n = 0
function App() {
useEffect(() => console.log(n), [n])
const tap = () => {
n++
}
return <button onClick={tap}>xxx{n}</button>
}
export default App
我们在修改变量n时,触发重新渲染,此时可以监听到变量n
import { useEffect, useState } from 'react'
let n = 0
function App() {
const [_, render] = useState({})
useEffect(() => console.log(n), [n])
const tap = () => {
n++
render({})
}
return <button onClick={tap}>xxx{n}</button>
}
export default App
手写
v1.0
注意:
useEffect(callback)
😅组件每次加载时, callback 就会执行一次,
😅但实际情况是, 组件每次加载 callback 会执行两次,
😅这就涉及到 V2.0的功能: 监听数据变化前, 多出来的一次执行是为了存值
let pre: any[] = []
function useEffect_(cb: Function, arr: any[]) {
// 没有依赖, useEffect执行, cb就执行
if (!arr) {
cb()
return
}
// 如果有依赖, 依赖变化就执行
if (hasChanged(pre, arr)) {
cb()
pre = arr
}
}
function hasChanged(pre: any[], arr: any[]) {
for (let i = 0; i <= arr.length - 1; i++) {
if (arr[i] !== pre[i]) return true
}
return false
}
function App() {
const [n, setN] = useState(0)
useEffect_(() => {
console.log(n, '---------');
}, [n])
const tap = () => {
setN(n + 1)
}
return <button onClick={tap}>xxx{n}</button>
}
export default App
v2.0
- 新增攻能: 监听数据变化前
- 新增了该功能后, 组件每次加载时, 回调函数都会执行两次
// 依赖是否发生变化
function hasChanged(pre: any[], arr: any[]) {
for (let i = 0; i <= arr.length - 1; i++) {
if (arr[i] !== pre[i]) return true
}
return false
}
let pre: any[] = []
let before: Function = null
function useEffect_(after: Function, arr: any[]) {
const run = () => {
if (!before) before = after()
before()
before = after()
pre = arr
}
// 没有依赖, useEffect执行, cb就执行
if (!arr) {
run()
return
}
// 如果有依赖, 依赖变化就执行
if (hasChanged(pre, arr)) {
run()
}
}
function App() {
const [n, setN] = useState(0)
useEffect_(() => {
console.log(n, '---------after');
return () => console.log(n, '-------before');
}, [n])
const tap = () => {
setN(n + 1)
}
return <button onClick={tap}>xxx{n}</button>
}
export default App
v2.0的bug: 使用了两次useEffect就会出问题
function App() {
const [n, setN] = useState(0)
useEffect_(() => {
console.log(n, '---------after');
}, [n])
useEffect_(() => {
console.log(n, '---------after');
}, [n])
const tap = () => {
setN(n + 1)
}
return <button onClick={tap}>xxx{n}</button>
}
useEffect中的闭包
1. 打印变化前的值
const [n, setN] = useState(0)
useEffect(() => {
console.log(n, '------ after');
return () => console.log(n, '-------- before'); // 打印变化前的值
})
useEffect原理
let before: Function = null
function useEffect(after: Function) {
if (!before) before = after()
before()
before = after()
}
解释
let before: Function = null
// 第一次执行
{
const n = 0
const after = () => {
console.log(n)
return () => console.log(n)
}
before = after() // 打印0
before() // 打印0
before = after() // 打印0
}
// 第二次执行
{
const n = 1
const after = () => {
console.log(n)
return () => console.log(n)
}
before() // 打印: 0, 这是第一次执行after返回的函数
before = after() // 打印: 1
}
// 第三次执行
{
const n = 2
const after = () => {
console.log(n)
return () => console.log(n)
}
before()
before = after()
}
2. 获取不到最新值
useEffect中的异步获取不到最新值
export default function App() {
const [count, setCount] = useState(1);
useEffect(() => {
const timer = setInterval(() => {
console.log(count); // 这里永远都是 0
setCount(count + 1);
}, 1000);
return () => clearInterval(timer);
}, []);
return (
<div className="App">
<h2>{count}</h2>
</div>
);
}
原因
// App 第一次执行时的内容
{
const count = 1
setInterval(() => {
console.log(count); // 这里永远都是 0
setCount(count + 1);
}, 1000);
}
// App 第二次执行时的内容
{
const count = 2
}