React本质上是建立用户界面的库。一个公式有助于理解React:view=function(state)
,或简写为v=f(s)
。下一个问题是:React在什么时间、如何更新视图?回答这个问题之前,我们先弄清楚——什么是渲染?
本文内容来自React.gg。
什么是渲染(rendering)
长话短说,渲染是指React调用部件(Component)更新视图。
React渲染部件的时候会发生两件事。首先React会为需要渲染的部件创建快照,这个快照包含属性、状态、事件处理函数,以及UI的描述。
为了得到你的应用的初始UI,React需要做初始的渲染,这个初始渲染发生在root
上。
import { createRoot } from "react-dom/client";
import App from "./App";
const rootElement = document.getElementById("root");
const root = createRoot(rootElement);
root.render(<App />);
当然,有趣的事都发生在初始渲染之后的子渲染上。那么,到底React在什么时候重新渲染一个部件?像上面公式所示,当s
变化的时候,f
被激活。
React什么时候重新渲染(re-rendering)
触发React部件重新渲染的唯一条件是状态的改变。React怎么知道部件的状态发生了改变?与上面提到的快照有关。当事件处理函数(event handler)被激活,函数会访问部件的属性(props)和状态(state),这些属性和状态都已经被保存在快照里的。
如果事件处理函数包含改变状态的内容,React会比较新的状态与快照中保存的状态,如果状态发生改变,会处罚部件的的重新渲染——创建新的快照,更新视图。
现在我们已经建立了React渲染原理的心智模型,接下来是实践时间。假设我们需要一个简单的应用,用户点击按钮后切换不同的问候语。为了实现这个功能,我们将问候语放入一个数组,然后用状态index
存储当前的问候语。用户点击按钮后,或者增加index
的值,如果到达数组最后一个元素,则将其重置为0
。代码如下:
import * as React from "react"
function Greeting ({ name }) {
const [index, setIndex] = React.useState(0)
const greetings = ['Hello', "Hola", "Bonjour"]
const handleClick = () => {
const nextIndex = index === greetings.length - 1
? 0
: index 1
setIndex(nextIndex)
}
return (
<main>
<h1>{greetings[index]}, {name}</h1>
<button onClick={handleClick}>Next Greeting</button>
</main>
)
}
export default function App () {
return <Greeting name="Tyler" />
}
现在只要按钮被点击,handleClick
事件处理程序就会运行。handleClick
中的状态index
与最近的快照中的状态相同。事件处理程序中React看到有一个对setIndex
的调用,并且传递给它的值与快照中的状态不同,因此触发了重新渲染。
现在看另一段代码:
代码语言:javascript复制import * as React from "react"
export default function VibeCheck () {
const [status, setStatus] = React.useState("clean")
const handleClick = () => {
setStatus("dirty")
alert(status)
}
return (
<button onClick={handleClick}>
{status}
</button>
)
}
这个例子与上面的没有什么不同。
当handleClick
事件处理程序运行时,它访问快照创建时的props
和state
——在那个时刻,state
的值是clean
。因此提醒的状态是clean
。
再次点击按钮,因为之前的按钮点击触发了重新渲染,并创建了一个新的快照,其状态为dirty
,在最初的点击之后的任何点击中,我们都会得到dirty
。
继续,下面的代码中,点击按钮后会发生什么?
代码语言:javascript复制import * as React from "react"
export default function Counter () {
console.count("Rendering Counter")
const [count, setCount] = React.useState(0)
const handleClick = () => {
console.count("click")
setCount(count)
}
return (
<button onClick={handleClick}>