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,它仅会在某个依赖项扭转时才从新计算缓存值。这种优化有助于防止在每次渲染时都进行高开销的计算。