4、组件状态管理:useState Hook详解、状态提升、状态与props的区别、不可变数据原则
好,咱们进入第四讲。这一章可以说是 React 组件化思维的核心中的核心——状态管理。
很多新手写 React,写着写着就乱了。为什么?因为对状态的理解不够深。我见过太多人把状态当全局变量用,或者把 props 当状态改。今天咱们就把这些坑一个一个填上。
4.1 useState Hook 详解
先说说 useState。这是 React 16.8 引入的 Hook,说白了就是让函数组件也能拥有自己的状态。
用法很简单:
const [count, setCount] = useState(0);
左边是状态值和更新函数,右边是初始值。嗯,这里要注意:初始值只在首次渲染时生效。
我举个例子:
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>你点击了 {count} 次</p>
<button onClick={() => setCount(count + 1)}>+1</button>
</div>
);
}
每次点击按钮,setCount 会触发重新渲染。React 会记住最新的 count 值,而不是重新初始化。
4.2 状态与 props 的区别
这个问题我面试时必问。很多人答不上来,或者答得模棱两可。
其实很简单:
- props 是父组件传进来的,组件内部不能改
- state 是组件自己管理的,可以随时更新
打个比方:props 就像你从家里带的盒饭,你不能在食堂里把它变成汉堡。state 就像你到食堂后点的菜,想吃啥点啥,还能加辣。
看个实际例子:
function UserCard({ name, age }) {
// name 和 age 是 props,不能改
const [isEditing, setIsEditing] = useState(false);
// isEditing 是 state,可以改
return (
<div>
<p>{name} - {age}岁</p>
<button onClick={() => setIsEditing(!isEditing)}>
{isEditing ? '保存' : '编辑'}
</button>
</div>
);
}
4.3 状态提升
这是 React 里一个非常重要的模式。说白了就是:把多个组件共享的状态,提到它们最近的共同父组件里。
为什么要这么做?
你想想看,两个兄弟组件要共享数据怎么办?总不能一个改另一个的 state 吧?那太乱了。正确的做法是把状态放到父组件,然后通过 props 往下传。
来看个经典例子——温度转换器:
function TemperatureInput({ scale, temperature, onTemperatureChange }) {
return (
<fieldset>
<legend>{scale === 'c' ? '摄氏' : '华氏'}温度</legend>
<input
value={temperature}
onChange={(e) => onTemperatureChange(e.target.value)}
/>
</fieldset>
);
}
function Calculator() {
const [temperature, setTemperature] = useState('');
const [scale, setScale] = useState('c');
const celsius = scale === 'f' ? tryConvert(temperature, toCelsius) : temperature;
const fahrenheit = scale === 'c' ? tryConvert(temperature, toFahrenheit) : temperature;
function handleCelsiusChange(value) {
setScale('c');
setTemperature(value);
}
function handleFahrenheitChange(value) {
setScale('f');
setTemperature(value);
}
return (
<div>
<TemperatureInput
scale="c"
temperature={celsius}
onTemperatureChange={handleCelsiusChange}
/>
<TemperatureInput
scale="f"
temperature={fahrenheit}
onTemperatureChange={handleFahrenheitChange}
/>
</div>
);
}
看到没?温度状态放在 Calculator 里,两个 TemperatureInput 通过 props 接收和修改。这就是状态提升。
4.4 不可变数据原则
这个原则太重要了。我刚开始写 React 时,经常直接修改 state 里的对象或数组,结果界面死活不更新。
为什么?因为 React 用的是浅比较。你直接修改对象属性,引用没变,React 就认为没变化。
正确的做法是:每次更新都返回一个新的对象或数组。
看几个例子:
// ❌ 错误:直接修改 state
const [user, setUser] = useState({ name: '张三', age: 25 });
user.age = 26; // 这样不行!
setUser(user); // 界面不会更新
// ✅ 正确:创建新对象
setUser({
...user,
age: 26
});
// 数组也一样
const [items, setItems] = useState([1, 2, 3]);
// ❌ 错误
items.push(4);
setItems(items);
// ✅ 正确
setItems([...items, 4]);
// 删除元素
setItems(items.filter(item => item !== 2));
// 更新元素
setItems(items.map(item =>
item === 2 ? 200 : item
));
4.5 总结一下
这一章咱们聊了四个核心概念:
- useState:函数组件的状态管理工具,简单但强大
- 状态 vs props:一个可变一个不可变,别搞混了
- 状态提升:共享状态往上提,各组件各司其职
- 不可变数据:永远不要直接修改 state,创建新值才是正道
这些概念看起来简单,但真正用好需要大量实践。我建议你从现在开始,每次写组件时都问自己三个问题:
- 这个数据应该是 state 还是 props?
- 这个状态应该放在哪个组件?
- 我更新状态时,有没有创建新对象?
养成这个习惯,你的 React 代码会越来越清晰。下一章咱们聊 useEffect 和副作用管理,到时候见。