zustand
安装
bash
npm install zustand -S
基本使用
编写状态和动作
src/store/appStore.js
ts
/**
* @Author: jsopy
* @Date: 2024-12-25 13:45:52
* @LastEditTime: 2024-12-25 14:18:23
* @FilePath: /Zustanddemo1/src/store/appStore.js
* @Description:切片
* @
*/
import { create } from "zustand";
const useAppStore = create((set, get) => ({
count: 0,
price: 10,
increment: (value) => {
set((state) => {
const result = JSON.parse(JSON.stringify(state));
result.count += value;
return result;
});
},
decrement: (value) => {
set((state) => {
const result = JSON.parse(JSON.stringify(state));
result.count -= value;
return result;
});
},
gettotal: () => {
console.log(get().count);
console.log(get().price);
return get().count * get().price;
},
}));
export default useAppStore;
使用状态和动作
src/App.tsx
jsx
import useAppStore from "./store/appStore";
function App() {
const { count, price, increment, decrement, gettotal } = useAppStore();
return (
<div>
<h1>App</h1>
<p>{count}</p>
<p>{price}</p>
<p>{gettotal()}</p>
<button onClick={() => increment(4)}>增加4个</button>
<button onClick={() => decrement(4)}>减少4个</button>
</div>
);
}
export default App;
补充 ts 类型声明
jsx
import { create } from "react";
type AppleStateType = {
price: number,
count: number,
increment: () => void,
decrement: () => void,
gettotal: () => number,
};
const useAppStore = create<AppleStateType>()((set, get) => ({
count: 0,
price: 10,
increment: (value) => {
set((state) => {
const result = JSON.parse(JSON.stringify(state));
result.count += value;
return result;
});
},
decrement: (value) => {
set((state) => {
const result = JSON.parse(JSON.stringify(state));
result.count -= value;
return result;
});
},
gettotal: () => {
console.log(get().count);
console.log(get().price);
return get().count * get().price;
},
}));
export default useAppStore;
高级版中间件
immer
注意
immer 是一个不可变数据管理的库,可以让我们在修改数据的时候,不需要深拷贝,直接修改数据,然后返回一个新的对象
- 安装依赖
bash
npm install immer -S
- 使用方式
jsx
/**
* @Author: jsopy
* @Date: 2024-12-25 13:45:52
* @LastEditTime: 2024-12-25 14:38:37
* @FilePath: /Zustanddemo1/src/store/appStore.js
* @Description:切片
* @
*/
import { create } from "zustand";
import { immer } from "zustand/middleware/immer";
const useAppStore = create(
immer((set, get) => ({
count: 0,
price: 10,
detail: {
name: "jsopy",
age: 30,
},
increment: (value) => {
set((state) => {
state.count += value;
});
},
decrement: (value) => {
set((state) => {
state.count -= value;
});
},
gettotal: () => {
console.log(get().count);
console.log(get().price);
return get().count * get().price;
},
setName: (value) => {
set((state) => {
state.detail.name = value;
});
},
setAge: (value) => {
set((state) => {
state.detail.age = value;
});
},
}))
);
export default useAppStore;
异步动作
(该怎么写就怎么写)
jsx
import { create } from "zustand";
import { immer } from "zustand/middleware/immer";
const useAppStore = create(
immer((set, get) => ({
count: 0,
price: 10,
detail: {
name: "jsopy",
age: 30,
},
increment: (value) => {
set((state) => {
state.count += value;
});
},
decrement: (value) => {
set((state) => {
state.count -= value;
});
},
gettotal: () => {
console.log(get().count);
console.log(get().price);
return get().count * get().price;
},
setName: (value) => {
set((state) => {
state.detail.name = value;
});
},
setAge: (value) => {
set((state) => {
state.detail.age = value;
});
},
doubleCount: async () => {
let result = await new Promise((resolve, reject) => resolve(2));
set((state) => {
state.count *= result;
});
},
}))
);
- 组件里面使用
jsx
import useAppStore from "./store/appStore";
function App() {
const {
count,
price,
increment,
decrement,
setName,
setAge,
gettotal,
detail,
doubleCount,
} = useAppStore();
return (
<div>
<h1>App</h1>
<p>{count}</p>
<p>{price}</p>
<p>{gettotal()}</p>
<p>{detail.name}</p>
<p>{detail.age}</p>
<button onClick={() => doubleCount()}>数量翻倍</button>
<button onClick={() => increment(4)}>增加4个</button>
<button onClick={() => decrement(4)}>减少4个</button>
<button onClick={() => setName("李四")}>改变名字</button>
<button onClick={() => setAge(88)}>改变年龄</button>
</div>
);
}
export default App;
useShallow
注意
A,B 组件 A 组件改变 stor 状态,B 组件哪怕没用 A 组件里面的状态,B 也会重新渲染.这是不对的
这个时候就需要用 useShallow
- 使用
jsx
import { useShallow } from "zustand/shallow";
import { useAppStore } from "../store/appStore";
import { increment, decrement, doubleCount, getTotal } from "../store/appStore";
function Child1() {
const { price, count } = useAppStore(
useShallow((state) => {
return {
price: state.price,
count: state.count,
};
})
);
return (
<div>
<h1>价格是{price}</h1>
<h2>数量是{count}</h2>
<h1
onClick={() => {
increment(3);
}}
>
增加+3
</h1>
<h1
onClick={() => {
decrement(3);
}}
>
减去-3
</h1>
<h1
onClick={() => {
doubleCount();
}}
>
数量翻倍
</h1>
<h1>获取到总价{getTotal()}</h1>
</div>
);
}
export default Child1;
Redux DevTools
去谷歌商店里面安装 Redux Tolls
使用的时候就是 store 里面加入一层包裹
bash
devtools(回调,{enabled:true,name:"appstore"})
- 举例
jsx
import { create } from "zustand";
import { createJSONStorage, devtools, persist } from "zustand/middleware";
import { immer } from "zustand/middleware/immer";
interface AppStore {
price: number;
count: number;
}
export const useAppStore = create<AppStore>()(
immer(
devtools(
persist(
() => ({
price: 7.0,
count: 1,
}),
{
name: "appStore",
partialize: (state) => ({ count: state.count }),
storage: createJSONStorage(() => sessionStorage),
}
),
{ enabled: true, name: "appStore2" }
)
)
);
持久化
就是把数据保存到缓存中,在加一层包裹层
- 使用
jsx
immer(
devtools(persist(回调, { name: "appstore" }), {
enabled: true,
name: "appStore2",
})
);
- 举例
jsx
import { create } from "zustand";
import { createJSONStorage, devtools, persist } from "zustand/middleware";
import { immer } from "zustand/middleware/immer";
interface AppStore {
price: number;
count: number;
}
export const useAppStore = create<AppStore>()(
immer(
devtools(
persist(
() => ({
price: 7.0,
count: 1,
}),
{
name: "appStore",
partialize: (state) => ({ count: state.count }),
storage: createJSONStorage(() => sessionStorage),
}
),
{ enabled: true, name: "appStore2" }
)
)
);
- 仅存指定的属性
js
// 仅仅保存count属性
partialize: (state) => ({ count: state.count }),
- 指定某些属性不存储
这里[price]
就是黑名单
js
partialize: (state) => {
Object.fromEntries(
Object.entries(state).filter(([key]) => !["price"].includes(key))
)
},
// const obj ={aaa:"xxx",ccc:2000}
// Object.entries(obj) // 返回值 [['aaa','xxx'],['ccc',2000]]
//const arr = [['aaa','xxx'],['ccc',2000]]
//Object.fromEntries(arr) // 返回值 {aaa:'xxx',ccc:2000}
- 指定存储位置
js
import { createJSONStorage, devtools, persist } from "zustand/middleware";
{
name: "appStore",
partialize: (state) => ({ count: state.count }),
storage: createJSONStorage(() => sessionStorage),
}
- 清除 storage
js
/* 清除本地存储,但是状态本身(内存)并没有清除
如果此时对状态操作 会重新保存到本地存储
所以一般直接自己写个方法 还原成初始值
*/
useAppStore.persist.clearStorage;
订阅模式(少)
警告
假设在模板中判断状态是否满足条件,满足条件就一种渲染,不满足就另外一种渲染
直接使用状态 状态只要改变 我就重新渲染,不管它在判断中是否成立
- 改用订阅模式
状态改变,组件不刷新,只有当状态改变后并且条件判断成立的时候,再刷新
ts
// 放在useEffect中,目的是仅仅订阅一次
useEffect(() => {
const unsub = useAppStore.subscribe((state, prevState) => {});
return unsub;
}, []);
- 监听具体属性
bash
immer(
devtools(
subscribeWithSelector(
persist(回调,{配置})
)
)
)
- 对具体属性订阅
js
useEffect(() => {
const unsub = useAppStore.subscribe(
state=>state.count,
(count,prevCount)=>{...},
{
equalityFn:shallow, // 判断两个对象是否相等
fireImmediately:true // 是否立即执行回调函数
}
);
return unsub;
}, []);
getState/setState
把方法可以拿出来,,代码逻辑清晰了
jsx
import { create } from "zustand";
import { createJSONStorage, devtools, persist } from "zustand/middleware";
import { immer } from "zustand/middleware/immer";
export const useAppStore = create()(
immer(
devtools(
persist(
() => ({
price: 7.0,
count: 1,
detail: {
name: "jsopy",
age: 18,
},
}),
{
name: "appStore",
partialize: (state) => ({ count: state.count }),
storage: createJSONStorage(() => sessionStorage),
}
),
{ enabled: true, name: "appStore2" }
)
)
);
export const decrement = (value) => {
useAppStore.setState((state) => {
state.count -= value;
});
};
export const increment = (value) => {
useAppStore.setState((state) => {
state.count += value;
});
};
export const getTotal = () => {
return useAppStore.getState().price * useAppStore.getState().count;
};
export const doubleCount = async () => {
await new Promise((resolve, reject) => {
resolve(2);
});
useAppStore.setState((state) => {
state.count *= 2;
});
};
export const setName = (value) => {
useAppStore.setState((state) => {
state.detail.name = value;
});
};
export const setAge = (value) => {
useAppStore.setState((state) => {
state.detail.age = value;
});
};
- 组件里面使用
jsx
import { useAppStore } from "./store/appStore";
import { useShallow } from "zustand/shallow";
import {
doubleCount,
getTotal,
increment,
decrement,
setName,
setAge,
} from "./store/appStore";
function App() {
const { count, price, detail } = useAppStore(
useShallow((state) => {
return {
price: state.price,
count: state.count,
detail: state.detail,
};
})
);
return (
<div>
<h1>App</h1>
<p>{count}</p>
<p>{price}</p>
<p>{getTotal()}</p>
<p>{detail.name}</p>
<p>{detail.age}</p>
<button onClick={() => doubleCount()}>数量翻倍</button>
<button onClick={() => increment(4)}>增加4个</button>
<button onClick={() => decrement(4)}>减少4个</button>
<button onClick={() => setName("李四")}>改变名字</button>
<button onClick={() => setAge(88)}>改变年龄</button>
</div>
);
}
export default App;