1.概述
use-immer
不属于官方Hook
,是社区维护的第三方库!use-immer
通过封装 Immer 的不可变更新机制,为 React 开发者提供了一种更直观、高效的状态管理方式。- 它尤其适合处理复杂嵌套状态或需要频繁更新的场景,同时保持了与 React 原生 Hook 的兼容性。
- 对于追求代码简洁性和可维护性的项目,
use-immer
是一个值得尝试的解决方案。
PS:
- 为了解决
useState
与useReducer
, 操作对象及数据非常的麻烦的问题!有了use-immer
之后就不在需要这么麻烦了!- 因为它的底层是基于 immer.js 这个库去实现的!immer.js 库可以高效的去复制一个对象,并且在更改新的草稿对象的时候不会修改原始对象!
- 正好合 React 的理念相符合!所以说在工作中更多的应用中使用最多的就是这个库。
2.安装
pnpm
或者 npm
安装
// pnpm 安装
pnpm add use-immer
// npm 安装
npm install use-immer
3.使用指南
以下通过多个实际场景展示
use-immer
的强大功能涵盖基础状态更新、复杂对象/数组操作、表单管理以及与
useImmerReducer
的结合使用。
3.1 基础计数器
直接修改数值
说明:
- 直接传递新值(如
count + 1
)给setCount
,行为类似useState
,适合简单状态更新。
import React from "react";
import { useImmer } from "use-immer";
function Counter() {
const [count, setCount] = useImmer(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
<button onClick={() => setCount(count - 1)}>Decrement</button>
</div>
);
}
3.2 复杂对象更新
深层嵌套修改简化
import { useImmer } from 'use-immer';
interface User {
name: string;
address: {
city: string;
zip: string;
};
}
function App() {
const [user, updateUser] = useImmer<User>({
name: "Alice",
address: {
city: "New York",
zip: "10001",
},
});
const updateCity = () => {
updateUser(draft => {
// 使用 Immer 的 draft 草稿对象直接修改嵌套的城市字段,无需手动复制其他字段
draft.address.city = "Los Angeles";
});
};
return (
<div>
<p>Name: {user.name}</p>
<p>City: {user.address.city}</p>
<button onClick={updateCity}>Change City</button>
</div>
);
}
export default App;
对比原生 useState
// 原生方式需手动展开所有嵌套层级
const [user, setUser] = useState(initialUser);
setUser({
...user,
address: {
...user.address,
city: "Los Angeles",
},
});
3.3 处理数组
数组操作变得异常简单,所有原生数组方法都可以直接使用
import { useImmer } from 'use-immer';
interface User {
name: string;
age: number;
}
function App() {
// 修正初始值类型,应为 User[] 数组类型
const [arr, updatArr] = useImmer<User[]>([
{
name: "Alice",
age: 18,
}
]);
const updateCity = (i: User) => {
updatArr(draft => {
// 新增数据
draft.push(i);
// PS: 这里也可以直接修改值 draft[0].age = 60;
});
};
return (
<div>
{
Array.isArray(arr) && arr.map((item, index) => (
<div key={index}>
<p>Name: {item.name}</p>
<p>Age: {item.age}</p>
</div>
))
}
<button onClick={() => updateCity({ name: "Bob", age: 20 })}>Add User</button>
</div>
);
}
export default App;
3.4 表单管理
优势:
- 无需为每个字段单独维护
useState
,代码更简洁。 - 动态字段更新(如
draft[name] = value
)避免冗余逻辑。
function ContactForm() {
const [form, updateForm] = useImmer({
name: "",
email: "",
phone: "",
});
const handleChange = (e) => {
const { name, value } = e.target;
updateForm(draft => {
draft[name] = value; // 动态更新任意字段
});
};
const handleSubmit = (e) => {
e.preventDefault();
console.log("Form submitted:", form);
};
return (
<form onSubmit={handleSubmit}>
<input
name="name"
value={form.name}
onChange={handleChange}
placeholder="Name"
/>
<input
name="email"
value={form.email}
onChange={handleChange}
placeholder="Email"
/>
<input
name="phone"
value={form.phone}
onChange={handleChange}
placeholder="Phone"
/>
<button type="submit">Submit</button>
</form>
);
}
3.5 高级场景:useImmerReducer
管理复杂状态
为什么选择 useImmerReducer
:
- 集中管理相关状态逻辑
- 避免
useReducer
中手动展开状态的繁琐操作。
import { useImmerReducer } from 'use-immer';
// 定义状态类型,增加点属性
interface CounterState {
name: string;
age: number;
}
// 定义 action 类型,增加与点属性相关的 action
type CounterAction = { type: 'increment' } | { type: 'decrement' } | { type: 'reset' };
// 定义 reducer 函数,增加点属性的处理逻辑
function counterReducer(draft: CounterState, action: CounterAction): CounterState { // 增加返回值类型声明
switch (action.type) {
case 'increment':
draft.age++;
break;
case 'decrement':
draft.age--;
break;
case 'reset':
draft.age = 0;
break;
}
return draft; // 明确返回 draft
}
function App() {
// 使用 useImmerReducer 初始化状态,将初始化值改为对象形式,增加点属性的初始化
const initialState: CounterState = { name: 'Jon', age: 18 }; // 增加类型注解
const [state, dispatch] = useImmerReducer(counterReducer, initialState);
const { name, age } = state;
return (
<div>
<p>姓名: {name}</p>
<p>年龄: {age ?? 0}</p>
<button onClick={() => dispatch({ type: 'increment' })}>增加</button>
<button onClick={() => dispatch({ type: 'decrement' })}>减少</button>
<button onClick={() => dispatch({ type: 'reset' })}>重置</button>
</div>
);
}
export default App;
4.总结
use-immer
通过隐藏不可变更新的复杂性,让开发者能更专注于业务逻辑,尤其适合中大型 React 项目。
use-immer
与原生 useState
/useReducer
对比?
特性 | use-immer | useState | useReducer |
---|---|---|---|
状态更新方式 | 直接修改草稿(draft ) |
返回新状态对象 | 返回新状态对象 |
深层嵌套处理 | 无需手动展开,直接修改 | 需手动展开或使用工具库(如 immer ) |
需手动展开或使用工具库(如 immer ) |
代码可读性 | 高(类似直接赋值) | 低(需处理嵌套) | 中(需编写 reducer 逻辑) |
性能优化 | 自动生成不可变更新,减少重渲染 | 依赖开发者优化 | 依赖开发者 |
何时使用 use-immer
?
场景 | 推荐 Hook |
---|---|
简单状态(数值、字符串) | useState |
深层嵌套对象/数组更新 | useImmer |
表单或多字段同步管理 | useImmer |
复杂状态逻辑(如购物车) | useImmerReducer |