翻译自 Zustand: simple, modern state management for React , 本人英语水平一般, 借助了谷歌翻译和自己的理解, 大概翻译出了全文, 如有错误, 请谅解, 下面所有代码可以查看 codesandbox
状态管理一直是现代程序应用中的重要组成部分, 在早期, 通过将数据传递给各种应用程序来管理状态, 但随着应用程序复杂性的增加, 这种方法开始变得低效, 为了解决这些, 不同的状态管理库被开发出来, 他们唯一的目标是创建一个中央的存储来管理这些状态, 在各个组件分发数据, 这样做的结果是有了清晰的代码和更好的数据处理和组件之间的共享. 在这个教程中, 读者会学习如何使用 Zustand
来管理 states
, 以在他们的应用程序中实现简洁的代码和更好的数据流
Zustand 作为一个状态管理库
Zustand 是由 Jotai 和 React springs 的开发人员构建的快速且可扩展的状态管理解决方案, Zustand 以简单被大家所知, 它使用 hooks 来管理状态无需样板代码
"Zustand" 只是德语的"state"
有很多的流行 React 状态管理工具, 但以下是您更喜欢使用 Zustand
的一些原因
- 更少的样板代码
- Zustand 只在 state 的值改变时渲染组件, 通常可以处理状态的改变而无需渲染代码
- 状态管理通过简单定义的操作进行集中和更新, 在这方面和 Redux 类似, 但是又和 Redux 不太类似, Redux 开发必须创建 reducer、action、dispatch来处理状态, Zustand 让它变得更加容易
- 使用 hooks 来管理 states, Hooks 在 react 中很流行, 因此是一个很受欢迎的状态管理库
- Zustand 使用简单使用和简单实现的代码
- 通过消除使用
Context Provides
从而使代码更短、更易读
启动一个 app
第一步就是创建一个新的React应用并且安装 Zustand
依赖, 运行下的的命令
npx create-react-app zustand
cd zustand
npm install zustand
现在, 在我们的项目文件中安装了我们的状态管理供我们使用, 现在我们需要定义一个 store
希望包含应用程序使用的所有状态和他们的函数, 我们在 App.js
文件中定义
import create from 'zustand'
// define the store
const useStore = create(set => ({
votes: 0
}))
以上, 我们创建了一个 store 来跟踪关于 votes 的状态, 初始值是 0, 在这里, 我们的 store 名字是 useStore
. 定义 store, 我们使用了 function create
从 Zustand
引入, 它接受一个回调函数来创建 store
访问 Store
在我们的应用中使用这个 state, 我们可以将创建状态的值绑定到 DOM 元素
const getVotes = useStore(state => state.votes);
return (
<div className="App">
<h1>{getVotes} people have cast their votes</h1>
</div>
);
在上面的代码中, 我们有一个变量 getVotes
, 这个变量包含了 state 的属性 votes 的值, 有了这个, 我们可以访问这个值放入到 h1 DOM
元素中展示这个值, 现在, 如果我们运行我们程序 npm start
, 我们可以在页面上看到结果
更新 state
除了访问状态的值, 我们也可以修改 store 来改变 votes 的初始值, 我们先创建两个额外的属性 addVotes
和 subtractVotes
, 前者的作用是每次增加 1, 后者减 1
const useStore = create(set => ({
votes: 0,
addVotes: () => set(state => ({ votes: state.votes + 1 })),
subtractVotes: () => set(state => ({ votes: state.votes - 1 })),
}));
下面我们在我们的程序中使用它们
const addVotes = useStore(state => state.addVotes);
const subtractVotes = useStore(state => state.subtractVotes);
<div className="App">
<h1>{getVotes} people have cast their votes</h1>
<button onClick={addVotes}>Cast a vote</button>
<button onClick={subtractVotes}>Delete a vote</button>
</div>
addVotes
会更新 votes 的值 加1, subtractVotes
会更新 votes 的值减1, 因此任何时间点击button, 都会更新 state
访问存储状态
当我们定义上面的状态时, 我们使用 set()
方法, 假设我们在一个程序里, 我们需要存储 其他地方
的值添加到我们的状态, 为此, 我们将使用 Zustand
提供的方法 get()
代替, 此方法允许多个状态使用相同的值
// 第二个参数 get
const useStore = create((set,get) => ({
votes: 0,
action: () => {
// 使用 get()
const userVotes = get().votes
// ...
}
}));
处理异步数据
Zustand
让存储异步数据变得容易, 这里, 我们只需要发出 fetch
请求和 set()
方法来设置我们的状态值
const useStore = create((set) => ({
Votes: {},
fetch: async (voting) => {
const response = await fetch(voting)
set({ Votes: await response.json() })
},
}))
当 async
函数返回值, Votes 被分配返回的值, 我们使用 GitHub API
来演示, 如下所示
const voting = "https://api.github.com/search/users?q=john&per_page=5";
const useStore = create((set) => ({
voting: voting,
Votes: {},
fetch: async () => {
const response = await fetch(voting);
const json = await response.json();
set({ Votes: json.items })
},
}))
在上面的代码中, 这个 URL 对 github api 进行调用, 返回对应的值, store
会等待获取请求, 然后更新值, 我们可以将 URL 作为参数传递给状态的 fetch 属性, 如下所示
import create from "zustand";
const useStore = create((set, get) => ({
votes: 0,
addVotes: () =>
set((state) => ({
votes: state.votes + 1
})),
subtractVotes: () =>
set((state) => ({
votes: state.votes - 1
})),
fetch: async (voting: any) => {
const response = await fetch(voting);
const json = await response.json();
set({
votes: json.items.length
});
}
}));
export { useStore };
import { useState } from "react";
import { useStore } from "./store";
const voting = "https://api.github.com/search/users?q=john&per_page=5";
export default function App() {
const getVotes = useStore((state) => state.votes);
const addVotes = useStore((state) => state.addVotes);
const subtractVotes = useStore((state) => state.subtractVotes);
const fetch = useStore((state) => state.fetch);
return (
<div className="App">
<h1>{getVotes} People</h1>
<button onClick={addVotes}>Cast a vote</button>
<button onClick={subtractVotes}>Delete a vote</button>
<button
onClick={() => {
fetch(voting);
}}
>
Fetch votes
</button>
</div>
);
}
上面的代码中, API 返回的 items 的长度显示在 h1
元素中, 并且这个按钮有一个 onClick
事件, 该事件在状态的 fetch
属性中运行该函数, voting
当做参数传递过去, 使用 Zustand
, 一旦异步请求完成, 状态就会更新
在状态中访问和存储数组
假设我们需要在 Zustand
中存储一个 state 中的数组, 我们可以像下面这样定义
const useStore = create(set => ({
fruits: ['apple', 'banana', 'orange'],
addFruits: (fruit) => {
set(state => ({
fruits: [...state.fruits, fruit]
}));
}
}));
以上, 我们创建了一个 store
包含了 fruits state
, 其中包含了一系列水果, 第二个参数是 addFruits
, 接受一个参数 fruit
并运行一个函数来得到 fruits state
和 新增的 fruits
, 第二个变量用于更新我们存储状态的值
在我们应用中访问状态, 我们需要循环数组来返回我们所有的水果, 我们还可以通过 input
字段来更新
const fruits = useStore((state) => state.fruits);
const addFruits = useStore((state) => state.addFruits);
const inputRef = useRef();
const addFruit = () => {
addFruits(inputRef.current.value);
inputRef.current.value = "";
};
return (
<div className="App">
<h1>I have {fruits.length} fruits in my basket</h1>
<p>Add a new fruit</p>
<input ref={inputRef} />
<button onClick={addFruit}>Add a fruit</button>
{fruits.map((fruit) => (
<p key={fruit}>{fruit}</p>
))}
</div>
);
持续状态
状态管理库的一个共同特点是持久化状态, 例如: 在有 form
的网站中, 你希望保存用户信息, 如果用户不小心刷新了页面, 你会丢失所有数据记录. 在我们的应用中, 刷新时, 添加到状态的数据会丢失
Zustand
提供了持久化状态以防止数据丢失的功能, 这个功能, 我们将使用 Zustand
提供的名为 persist
的中间件, 该中间件通过 localStorage
来持久化来自应用程序的数据, 这样, 当我们刷新页面或者完全关闭页面时, 状态不会重置
import {persist} from "zustand/middleware"
// and modify our existing state
let store = (set) => ({
fruits: ["apple", "banana", "orange"],
addFruits: (fruit) => {
set((state) => ({
fruits: [...state.fruits, fruit],
}));
},
});
// persist the created state
store = persist(store, {name: "basket"})
// create the store
const useStore = create(store);
在上面的代码中, 我们持久化了 store
的值, localStorage
的 key 设为 basket
, 有了这个, 我们在刷新页面时不会丢失新增的数据, 永久保存(即: 在执行清除本地存储的操作之前, 状态保持不变)
另外: Zustand
提供了一个中间件来使用 Redux
开发工具扩展从浏览器查看状态值, 这个, 我们 import devtools from 'zustand/middleware'
, 像使用 持久化的方法使用它
store = devtools(store)
结论
本教程告诉读者如何在 React
中使用 Zustand
管理状态, Zustand
提供了一种简单的方式来访问和更新 state