React - 机制
Chapter 1
-
渲染与变量
-
React的函数式组件会在每次状态发生变化时,重新执行。如果在hook中声明了普通的变量,那么会在每次状态发生变化重新渲染时,重新进行初始化。
-
export default function() { let x = 2; // 点击后x虽然加1了,但是由于没有被React监听,所以页面并不会重新渲染 return <div onClick={() => x += 1}>{x}</div> }
-
React渲染条件是状态发生变化时,才会重新渲染。所以需要引用
useState
来获取一个可以创建变量被React监听的函数。(普通变量即使更新,React也不会触发渲染) -
import { useState } from 'react'; export default function() { const [ count, setCount ] = useState(0); let x = 1; // 点击后 x 虽然会加2,并且有被React监听的 count 变量。count + 1发生重新渲染,但是重新渲染导致 x 又被初始化为 1 return ( <div onClick={() => { x += 2; setCount(count + 1); }}></div> ) }
-
-
React的渲染阶段
- 分为三个阶段:触发、渲染、提交
- 触发只有两种:初次渲染、状态更新
- 渲染:初次渲染会调用React的根组件
createRoot
来创建组件,后续只有更新状态才会触发React的重新渲染。 - 提交:React会使用DOM Diff算法来减少重复渲染,只更新状态有变化的组件的DOM
-
State
-
由React提供的hook,声明的state只会在初次渲染的时候进行初始化,之后都会保持上次的结果值。因为它是被挂载在函数外类似于架子上,以确保每次渲染后也能拿到上次的值。
-
触发
setState
后,React会先挂载起来。然后会返回一个快照,就是state改变前的值。在该作用域下使用的state值都会是这个快照值,即使使用的时候state已经更新! -
export default function() { const [count, setCount] = useState(0); return ( <div onClick={async () => { setCount(count + 1); // 即使这个接口在state更新后才调用,也会使用state的快照,也就是0 await new Promise( (resolve) => setTimeout( () => resolve(count) , 999999)); }}></div> ) }
-
如果在同一个作用域下同时调用相同的多个
setState
,React会进行批处理,值执行最后一个setState
-
export default function() { const [count, setCount] = useState(0); return ( <div onClick={() => { setCount(count + 1); setCount(count + 2); // 只会执行最后一次的 setCount 也就是 3 setCount(count + 3); }}></div> ) }
-
执行
async
函数,即使在state发生改变了,接口还没await
完成也不影响该函数的继续完成 -
export default function() { const [count, setCount] = useState(0); return ( <div onClick={async () => { setCount(1); console.log('enter'); // 假设该接口会在count状态更新后,才执行完成 await getApi(); console.log('out'); // 结果是: enter、 count值改变、 getApi执行完成、 out }}></div> ) }
-
-
ref
和state
的区别-
相同点:都会挂载在函数外的架子上,确保每次渲染后,值不会被初始化。
-
不同点:ref其实就是一个包裹的普通对象
{ current: value }
,并且在更改ref值后并不会触发React的重新渲染。ref可以在任何阶段改变值,并且确保他能被实时更新。而state是异步更新。 -
export default function() { const [count, setCount] = useState(0); const num = useRef(null); return ( <div onClick={() => { num.current = 2; // 结果会是 0 + 2 = 2 setCount(count + num.current); }} ></div> ) }
-
-
ref像自定义组件传递ref
- 直接传递会无效,需要通过React的
forwardRef
来包裹该函数组件,然后该函数组件的参数将会添加ref,React.forwardRef((props, ref) => <div ref={ref}></div>)
- 直接传递会无效,需要通过React的
-
state同步更新
- 如果需要state进行同步更新,可以调用
react-dom
中的flushSync
,通过flushSync(callback)
该回调函数中的state更新都将同步
- 如果需要state进行同步更新,可以调用