在 React 中,state
的更新机制与 Vue 3 的 ref
确实存在本质区别:
Vue 3 的 ref
会对对象进行响应式代理,因此可以直接修改深层属性(如 obj.value.deep.prop = 123
),Vue 能自动检测到变化并触发更新。
React 的 state
则遵循 "不可变性原则",不能直接修改深层属性,必须通过创建新对象的方式更新(表面上看是 "整体修改",实际是替换引用)。
具体对比示例
Vue 3 中可以直接修改深层属性:
import { ref } from 'vue';
const user = ref({
info: {
name: '张三',
age: 20
}
});
// 直接修改深层属性,Vue 能检测到
user.value.info.age = 21; // 有效,视图会更新
React 中直接修改深层属性无效:
import { useState } from 'react';
const [user, setUser] = useState({
info: {
name: '张三',
age: 20
}
});
// 错误做法:直接修改深层属性,React 无法检测
const wrongUpdate = () => {
user.info.age = 21; // 无效,视图不会更新
setUser(user); // 引用未变,React 认为状态没变化
};
React 正确的深层更新方式:
必须创建新对象(保持引用变化):
const correctUpdate = () => {
setUser({
...user, // 复制外层对象
info: {
...user.info, // 复制内层对象
age: 21 // 修改目标属性
}
});
};
核心原因
- React 依赖引用变化检测状态更新(新旧状态引用不同才会触发重渲染)
- Vue 3 基于Proxy 代理,能追踪对象内部属性的变化,不需要依赖引用变化
如果觉得手动处理深层对象麻烦,可以使用 immer
库简化操作,它允许用 "直接修改" 的写法,但内部会自动转换为不可变更新:
import { produce } from 'immer';
const updateWithImmer = () => {
setUser(produce(draft => {
draft.info.age = 21; // 看似直接修改,实际是安全的
}));
};
总结:React 中不能像 Vue 3 那样直接修改深层属性,必须通过创建新对象的方式更新,但可以借助工具库简化这个过程。
import { useState } from 'react';
function UserProfile() {
// 定义深层对象状态
const [user, setUser] = useState({
name: '张三',
age: 25,
address: {
city: '北京',
district: '朝阳区',
street: '建国路88号'
},
hobbies: ['读书', '运动']
});
// 1. 修改浅层属性(一级属性)
const updateName = () => {
// 创建新对象,复制原有属性,只修改需要变更的部分
setUser({
...user, // 复制原有所有属性
name: '张三丰' // 覆盖需要修改的属性
});
};
// 2. 修改深层属性(二级及以上)
const updateCity = () => {
setUser({
...user, // 复制一级属性
address: {
...user.address, // 复制二级属性
city: '上海' // 覆盖需要修改的深层属性
}
});
};
// 3. 向深层数组添加元素
const addHobby = () => {
setUser({
...user,
hobbies: [...user.hobbies, '编程'] // 复制原数组并添加新元素
});
};
// 4. 复杂修改:同时修改多个层级的属性
const complexUpdate = () => {
setUser({
...user,
age: 26, // 修改一级属性
address: {
...user.address,
district: '浦东新区' // 修改二级属性
}
});
};
return (
<div>
<h3>用户信息</h3>
<p>姓名:{user.name}</p>
<p>年龄:{user.age}</p>
<p>城市:{user.address.city}</p>
<p>区:{user.address.district}</p>
<p>爱好:{user.hobbies.join(', ')}</p>
<button onClick={updateName}>修改姓名</button>
<button onClick={updateCity}>修改城市</button>
<button onClick={addHobby}>添加爱好</button>
<button onClick={complexUpdate}>复杂更新</button>
</div>
);
}
export default UserProfile;