状态提升
文本输入框旁边有一个预览框,需要显示输入框中的内容。它们都在同一个组件树里,但一个组件内声明的状态对它的兄弟组件是不可见的,所以输入框和预览框谁都看不到对方的数据,也改不了。解决办法就是把状态移到它们最近的公共父组件,然后以 props 的形式传递给两个子组件。这就叫状态提升。
下面是一对共享一个值的组件:
import { useState } from 'react'
function Form() {
const [text, setText] = useState('')
return (
<>
<TextInput text={text} onChange={setText} />
<TextDisplay text={text} />
</>
)
}
function TextInput({ text, onChange }) {
return <input value={text} onChange={e => onChange(e.target.value)} />
}
function TextDisplay({ text }) {
return <p>你输入了: {text}</p>
}Form 是 TextInput 和 TextDisplay 最近的公共父组件,所以 text 就住在这里。它把 text 传给两个子组件,还把 setText 作为 onChange prop 传给 TextInput。用户输入时,TextInput 调用 onChange 传入新值,Form 的状态更新,Form 重新渲染。这次重新渲染把新的 text 传回两个子组件,所以 TextDisplay 显示的就是输入框里的内容,自己不用管理任何状态。
让状态尽可能靠下
状态提升不是随时都要做的,只在真正需要时才用。基本原则就是状态共同位置:让每个状态尽可能地靠下放在组件树里,离真正使用它的组件近一点,只有在其他地方也需要读或改它时才向上提升。如果一个组件的状态没有其他组件触碰,就让它自己管理。
这很重要,因为提升是有代价的。每向上提一层,父组件就得把它作为 prop 传回下去,中间的每个组件都得接收并转发这个 prop,即便它们根本用不上这个值。先从局部开始。状态只向上提到真正需要共享它的最近组件,不要再往上了。
把修改函数传下去
注意 Form 不仅传了 text 下去,还传了 setText 下去,作为 onChange prop。这是状态提升的另一半:拥有状态的组件通过 props 提供一个修改状态的方式,子组件调用这个函数而不是自己管理值。TextInput 根本看不到 setText。它只是调用被传进来的那个函数,让父组件决定这个函数具体做什么。
对于嵌套很深的不同组件之间共享的状态,一层层传 props 会变得不实用。React 有针对这种情况的工具,Context 就是其中之一,Hooks 章节介绍的 useContext 可以在不逐层传递的情况下读取某个值。那超出本章范围;上面的模式是你首先应该用的。
接下来:副作用,你会用 useEffect 来处理渲染之外发生的工作。

