1.简单使用
安装zustand
npm install zustand
创建用来保存状态的 store.ts 文件
import { create } from 'zustand'
type State = {
count: number
}
type Actions = {
increment: (qty: number) => void
}
const useCountStore = create<State & Actions>((set) => ({
count: 0,
increment: (qty: number) => set((state) => ({ count: state.count + qty })),
}))
在app.ts组件中使用
function App() {
const count = useCountStore((state) => state.count)
const increment= useCountStore((state) => state.increment)
return (
<>
<div className="card">
<button onClick={() => increment(1)}>增加</button>
<p>当前值:{count}</p>
</div>
</>
)
}
2.一个保存用户语言的例子
store.ts
下面的代码使用了两个中间件
immer
:更加方便的操作对象,改变对象的值时,可直接操作对象
persist
:持久化存储,将对象存储到 localStorage
import { create } from 'zustand'
import { createJSONStorage, persist, subscribeWithSelector } from 'zustand/middleware';
import { immer } from 'zustand/middleware/immer';
// 定义类型
interface Lang {
code: string;
name: string;
}
// 定义初始值
const init: Lang = {
code: 'zh-CN',
name: '中文简体',
}
// 存储store的key
const storeKey = 'store-lang';
// 创建一个store
const useLangStore = create<Lang>()(
immer(
persist(
() => ({
...init
}),
{
// 持久化时存储到localStorage的key
name: storeKey,
// 存储方式,默认localStorage
storage: createJSONStorage(() => localStorage),
},
)
)
)
export default useLangStore;
// 设置语言
export const setLang = (state: Lang) => {
useLangStore.setState(state);
}
// 重置语言
export const resetLang = () => {
useLangStore.setState(init);
}
// 获取语言编码,通过这种方法获取,在组件里不会同步刷新状态,即当code改变时,不会触发组件渲染
export const getLangCodee = (): string => {
return useLangStore.getState().code;
}
app.ts
import { useState } from 'react'
import './app.css'
import useLangStore, { setLang } from './stores/lang/lang.store'
function App() {
const [inputLang, setInputLang] = useState('en-Es')
// 如果同时获取多个值可使用 useShallow
const langCode = useLangStore((state) => state.code)
const setLangData = () => {
setLang({
code: inputLang,
name: inputLang
});
}
return (
<>
<div className="card">
<button onClick={setLangData}>设置语言</button>
<p>
<input type="text" value={inputLang} onChange={(e) => setInputLang(e.target.value)} />
当前语言编码:{langCode}
</p>
</div>
</>
)
}
export default App
3.监听 localStorage 变化
通过监听 localStorage 的变化,可以使用当一个页签的内容发送改变时,其它页签的store也会发生改变
// 监听 localStorage 变化
window.addEventListener('storage', (event) => {
if (event.key === storeKey && event.newValue !== null) {
const val = JSON.parse(event.newValue);
// 这里判断用户状态,如果用户变化了,则进入登录页面或进入首页,根据具体业务自行处理
if (JSON.stringify(useLangStore.getState()) != JSON.stringify(val.state)) {
useLangStore.setState(val.state);
}
}
});
4.使用 useShallow 的例子
可以更加方便的同时获取多个值,而不会因为其它未使用到值的改变触发组件渲染
使用前
const langCode = useLangStore((state) => state.code)
const langName = useLangStore((state) => state.name)
使用后
const {code, name} = useLangStore(useShallow((state) => ({
code: state.code,
name: state.name
})))
5.使用订阅的例子
可优化组件渲染次数,如下示例,只有达到一定条件才会触发渲染,如果是在外层直接取出state的值,则state的值每次改变都会触发渲染
useEffect(() => {
return useCountStore.subscribe((state, prevState) => {
if (state.count >= 7 && prevState.count < 7) {
setText('超出')
} else if (state.count >= 7 && prevState.count < 7) {
setText('太少')
}
});
}, []);
6.更细粒度的订阅 subscribeWithSelector
上面的例子,在state每次改变都会触发逻辑判断,如果只想count改变的时候触发逻辑判断,可使用subscribeWithSelector
,需要在 create 的时候嵌套中间件 subscribeWithSelector
这里说明一下中间件顺序: immer
-> devtools
-> subscribeWithSelector
-> persist
本文未使用到 devtools
,devtools中间件可以帮助你在开发过程中调试和检查应用程序的状态变化,因为我们将状态存储到了localStorage,可直接通过浏览器控制台查看,故无需使用该中间件
示例
const useCountStore = create<Count>()(
immer(
subscribeWithSelector(
persist(
() => ({
...init
}),
{
// 持久化时存储到localStorage的key
name: storeKey,
// 存储方式,默认localStorage
storage: createJSONStorage(() => localStorage),
},
)
)
)
)
用法
useEffect(() => {
return useCountStore.subscribe(
state => state.count,
(count, prevCount) => {
if (count >= 7 && prevCount < 7) {
setText('超出')
} else if (count >= 7 && prevCount < 7) {
setText('太少')
}
});
}, []);
这样,就只有state中的count的值发生改变的时候,才会进入逻辑判断流程。