React 的外部更新机制?
`<h1 class=”name”> 我的名字是:Kyrie</h1>
<h1 class=”name”> 我的名字是:IRVING</h1>
`
React 也跟 vue 有着 Diff 算法,它每次 render 只会更新与上一次渲染不同的节点和内容,所以下面的例子,React 只须要这样做:
h1.innerText = Kyrie -> h1.innerText = IRVING
那 useEffect 呢?
`function Demo() {
const initCount = 0
const [count, setCount] = useState(initCount)
// 当咱们把 count 作为依赖传入时,count 扭转就会从新执行
useEffect(() => {
console.log(‘count 的值为:’, count)
}, [count])
return (
<div>
<h2>{count}</h2>
<button onClick={() => setCount(count + 1)}>count++</button>
</div>
)
}
`
所以 useEffect 的更新机制实现依附咱们传入的依赖,只有在 useEffect 里应用到的状态值都必须在依赖中申明,让 React 外部进行依赖更新。
所以,当依赖的状态变得多起来的时候,难免会让咱们在性能方面有所放心。
useEffect 传入依赖的正确形式?
当初有个需要:书写一个函数,每秒主动让 count+1
`function Demo() {
const initCount = 0
const [count, setCount] = useState(initCount)
useEffect(() => {
// 设定时器,每秒执行一次 setCount
const timer = setInterval(() => {
setCount(count + 1)
}, 1000)
return () => {
clearInterval(timer)
}
}, [])
return (
<div>
<h2>{count}</h2>
<button onClick={() => setCount(count + 1)}>count++</button>
</div>
)
}
`
下面的例子,咱们把 [] 作为 useEffect 的依赖,就是说 effect 只会在组件挂载后执行一次。定时器只须要设定一个,每秒 setInterval 后帮咱们把 count + 1 对吧,这不就能实现需求了吗
看起来貌似没什么问题,但运行起来发现 count 只自增了一次就停住了,也就是 useEffect 只执行了一次,what???
起因很简略,咱们把 [] 作为依赖,React 外部就会认为 effect 函数内没有依赖任何值,所以当 useEffect 第一次执行时,setCount(0 + 1) 此时 count = 1,而后 1s 后,setCount(1 + 1),count = 2?不,React 外部会主动疏忽第二次及当前的每次更新,因为咱们把 [] 作为依赖,就代表了 effect 只会执行一次,第二次开始,count 始终都是 1,这就导致了 count 变为 1 后就不减少了。
那好,咱们把 count 传入作为依赖
`useEffect(() => {
// 设定时器,每秒执行一次 setCount
const timer = setInterval(() => {
setCount(count + 1)
}, 1000)
return () => {
clearInterval(timer)
}
}, [count])
`
运行之后发现,问题的确解决了,count 胜利的每秒主动自增,但这其实不是最好的解决方案,因为咱们晓得每当 count 值扭转,就会触发 render,每次都会生成一个新的 useEffect,而后执行,从新生成一个定时器,尽管目标达到了,但这显然不是最优解,咱们最好能防止每次都生成一个新的定时器,因为这样咱们的定时器将毫无意义...
`useEffect(() => {
// 设定时器,每秒执行一次 setCount
const timer = setInterval(() => {
setCount(count => count + 1)
}, 1000)
return () => {
clearInterval(timer)
}
}, [])
`
能够看到,咱们把 count 从依赖中取出,而后在 setCount(count => count + 1),这样做得目标是为了通知 React,咱们只须要 count+1,并不需要读取它的值,因为 React 外部必定是晓得以后 count 的值,这样 effect 外部就不依赖 count 了,useEffect 只需执行一次即可,这也是 setState 的函数写法,函数的参数就是最新的状态值,如果不太理解这种写法的敌人能够去查一下材料,这里就不再过多论述了...
useEffect 配合 useReducer 应用?
这就是 useEffect 究极应用技巧,用法及其宽泛... 还是应用下面的例子,让咱们用 useReducer 改写一下
`function Demo() {
const initState = {count: 0}
const [state, dispatch] = useReducer(reducer, initState)
function reducer(state, action) {
switch(action.type) {
case ‘increment’ :
return {count: state.count + 1}
default:
throw new Error(‘type 不存在 …’)
}
}
useEffect(() => {
// 设定时器,每秒执行一次 setCount
const timer = setInterval(() => {
dispatch({type: ‘increment’})
}, 1000)
return () => {
clearInterval(timer)
}
}, [dispatch]) // 这里咱们依赖了 dispatch 其实能够省略
return (
<div>
<h2>{count}</h2>
<button onClick={() => setCount(count + 1)}>count++</button>
</div>
)
}
`
大家可能会问我,这种写法的益处是什么?其实,咱们利用 useReducer 通过 action 来形容行为,实现状态和行为的拆散,在多个依赖的时候这种写法的劣势就能很好的体现进去。
还有最初一个疑点:为什么 dispatch 能够省略?其实下面的例子,即便咱们把 dispatch 从依赖中取出,也能失常运行,effect 也只会执行一次。这么神奇?effect 是怎么晓得咱们的行为是什么?其实,React 外部会帮咱们记住 dispatch 的各种行为(action),且能拿到最新的 count,这一系列操作是 React 外部产生的,并不需要放在 effect 内。
useMemo 和 useCallback(性能优化)?
useCallback(缓存函数)
`const memoizedSetCount = useCallback(
() => {
setCount(count + 1)
},
[count],
);
`
把内联回调函数及依赖项数组作为参数传入 useCallback,它将返回该回调函数的缓存版本,该回调函数仅在某个依赖项扭转时才会更新。
useMemo(缓存值:相似于 Vue 的计算属性)
`const memoizedCount = useMemo(() => {
const doubleCount = count * 2
}, [count]);
`
把“创立”函数和依赖项数组作为参数传入 useMemo,它仅会在某个依赖项扭转时才从新计算缓存值。这种优化有助于防止在每次渲染时都进行高开销的计算。