更強(qiáng)大的React 狀態(tài)管理庫(kù)Zustand使用詳解
介紹
在這篇文章中,我會(huì)介紹 Zustand 在實(shí)際項(xiàng)目中的使用。
我會(huì)構(gòu)建一個(gè) GitHub 用戶(hù)搜索項(xiàng)目,在項(xiàng)目中通過(guò)調(diào)用 GitHub API 來(lái)實(shí)現(xiàn)搜索用戶(hù)的功能。我還會(huì)并演示如何直接從 Zustand 存儲(chǔ)中進(jìn)行 API 調(diào)用,并將狀態(tài)持久化到 sessionStorage 或 localStorage 中。
完成效果如下:

創(chuàng)建項(xiàng)目項(xiàng)目
首先,我們需要?jiǎng)?chuàng)建一個(gè)新的 React 應(yīng)用程序。
我們需要在終端中運(yùn)行命令來(lái)下面的命令來(lái)創(chuàng)建項(xiàng)目:
npx create-react-app github-user-search --template typescript
這行命令會(huì)利用 CRA 創(chuàng)建一個(gè)常規(guī)的 typescript react 項(xiàng)目。
安裝項(xiàng)目依賴(lài)
創(chuàng)建項(xiàng)目后,我們需要進(jìn)入該項(xiàng)目文件夾中。
cd github-user-search
并在終端中運(yùn)行以下命令來(lái)安裝項(xiàng)目所需要的依賴(lài)項(xiàng):
npm i zustand @chakra-ui/react @emotion/react @emotion/styled react-icons react-router-dom axios framer-motion pluralize query-string react-helmet-async react-hook-form react-paginate
- zustand:狀態(tài)管理庫(kù)。
- @chakra-ui/react:UI 庫(kù)。
- @emotion/react:CSS-in-JS 庫(kù)。
- @emotion/styled:CSS-in-JS 庫(kù)。
- react-icons:圖標(biāo)庫(kù)。
- react-router-dom:路由庫(kù)。
- axios:HTTP 請(qǐng)求庫(kù)。
- framer-motion:動(dòng)畫(huà)庫(kù)。
- pluralize:?jiǎn)卧~處理庫(kù)。
- query-string:URL 查詢(xún)字符串庫(kù)。
- react-helmet-async:React 異步線程安全庫(kù)。
- react-hook-form:React 表單庫(kù)。
- react-paginate:React 分頁(yè)庫(kù)。
創(chuàng)建項(xiàng)目結(jié)構(gòu)
安裝好項(xiàng)目依賴(lài)后,我們需要?jiǎng)?chuàng)建項(xiàng)目所需要的目錄結(jié)構(gòu)。
項(xiàng)目目錄結(jié)構(gòu)如下:
src ├── index.tsx ├── assets ├── container ├── components ├── pages ├── routes ├── services ├── store └── theme
設(shè)置環(huán)境變量
創(chuàng)建項(xiàng)目結(jié)構(gòu)后,我們需要?jiǎng)?chuàng)建環(huán)境變量。
在項(xiàng)目根目錄中創(chuàng)建一個(gè) .env 文件,并在其中添加以下變量:
REACT_APP_GITHUB_API_URL=https://api.github.com
服務(wù)
創(chuàng)建環(huán)境變量后,我們需要?jiǎng)?chuàng)建服務(wù)。
在項(xiàng)目的 services 文件夾中創(chuàng)建一個(gè) githubService.ts 文件,并添加以下代碼:
import axios from 'axios';
import qs from 'query-string';
const github = axios.create({
baseURL: process.env.REACT_APP_GITHUB_API_URL,
});
interface IGet {
url: string;
query?: Record<string, any>;
}
const get = async <T>({ url, query = {} }: IGet): Promise<T> => {
const queryString = `?${qs.stringify(query)}`;
const response = await github.get(`${url + queryString}`);
return response.data;
};
const methods = { get };
export default methods;
設(shè)置 store
接下來(lái),我們將建立我們的 Github store。我們將實(shí)現(xiàn) Zutsand persist 方法來(lái)將我們的狀態(tài)持久化到 sessionStorage 或 localStorage 中。
在 stores/github.ts 中添加代碼:
import create from 'zustand';
import { stringify } from 'query-string';
import { persist } from 'zustand/middleware';
import methods from 'services/github';
import { IUserDetails, ISearchResonse, IGithubStore } from 'stores/types';
export const githubStore = create(
persist<IGithubStore>(
(set, get) => ({
isLoading: false,
cached_users_details: [], // to cache users details
query: { page: 1, per_page: 20 },
search: async (query) => {
try {
set(() => ({ isLoading: true }));
window.history.pushState('', '', `?${stringify(query)}`);
const data = await methods.get<ISearchResonse>({
url: '/search/users',
query,
});
set(() => ({ data, query, isLoading: false }));
} catch (err: any) {
const error =
err?.message || err?.data?.message || 'Unexpected network error.';
set(() => ({ isLoading: false, error }));
}
},
getUser: async (username) => {
try {
set(() => ({ isLoading: true }));
// check if user is already cached
const userDetails = get().cached_users_details.find(
(u) => u.login === username
);
if (userDetails) {
set(() => ({ userDetails, isLoading: false }));
} else {
const userInfo = await methods.get<IUserDetails>({
url: `/users/${username}`,
});
set((state) => ({
cached_users_details: [...state.cached_users_details, userInfo],
userDetails: userInfo,
isLoading: false,
}));
}
} catch (err: any) {
const error =
err?.message || err?.data?.message || 'Unexpected network error.';
set(() => ({ isLoading: false, error }));
}
},
}),
{
name: 'search-storage',
getStorage: () => sessionStorage,
}
)
);
我們能夠?qū)?GitHub api 進(jìn)行異步調(diào)用,并直接從我們的 store 很好地處理響應(yīng),而無(wú)需使用額外的中間件。
我們還緩存了我們的用戶(hù)詳細(xì)信息,這樣我們就不必再次調(diào)用 API 來(lái)再次獲取相同的用戶(hù)詳細(xì)信息。我們還將我們的狀態(tài)持久化到會(huì)話存儲(chǔ)中。
我們還可以通過(guò)將 getStorage 方法的返回值更改為 localStorage,來(lái)將我們的狀態(tài)持久化到 localStorage 中。
清除/重置存儲(chǔ)
目前,Zustand 沒(méi)有關(guān)于清除/重置存儲(chǔ)開(kāi)箱即用的方法。
我們可以通過(guò)向我們的 store 添加一個(gè) clear/reset 方法,將我們的狀態(tài)重置回初始狀態(tài)并調(diào)用 sessionStorage 或 localStorage clear() 方法保持?jǐn)?shù)據(jù)的同步。
const initialState = {
data: undefined,
userDetails: undefined,
cached_users_details: [],
query: { page: 1, per_page: 20 },
isLoading: false,
error: undefined,
};
export const githubStore = create(
persist<IGithubStore>(
(set, get) => ({
...initialState,
...
clear: () => {
set(() => (initialState));
sessionStorage.clear(); // or localStorage.clear();
},
})
)
);
本文主要關(guān)注的是 Zustand 狀態(tài)管理部分,UI 部分就不展開(kāi)了。
以上就是更強(qiáng)大的React 狀態(tài)管理庫(kù)Zustand使用詳解的詳細(xì)內(nèi)容,更多關(guān)于React 狀態(tài)管理庫(kù)Zustand的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
React Native如何消除啟動(dòng)時(shí)白屏的方法
本篇文章主要介紹了React Native如何消除啟動(dòng)時(shí)白屏的方法,詳細(xì)的介紹了解決的方法,具有一定的參考價(jià)值,有興趣的可以了解一下2017-08-08
react 下拉框內(nèi)容回顯的實(shí)現(xiàn)思路
這篇文章主要介紹了react 下拉框內(nèi)容回顯,實(shí)現(xiàn)思路是通過(guò)將下拉框選項(xiàng)的value和label一起存儲(chǔ)到state中, 初始化表單數(shù)據(jù)時(shí)將faqType對(duì)應(yīng)的label查找出來(lái)并設(shè)置到Form.Item中,最后修改useEffect,需要的朋友可以參考下2024-05-05
react?Scheduler?實(shí)現(xiàn)示例教程
這篇文章主要為大家介紹了react?Scheduler?實(shí)現(xiàn)示例教程,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09
react+axios實(shí)現(xiàn)github搜索用戶(hù)功能(示例代碼)
這篇文章主要介紹了react+axios實(shí)現(xiàn)搜索github用戶(hù)功能,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-09-09
Header組件熱門(mén)搜索欄的實(shí)現(xiàn)示例
這篇文章主要為大家介紹了Header組件熱門(mén)搜索欄的實(shí)現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-04-04
react滾動(dòng)加載useInfiniteScroll?詳解
使用useInfiniteScroll?hook可以處理檢測(cè)用戶(hù)何時(shí)滾動(dòng)到頁(yè)面底部的邏輯,并觸發(fā)回調(diào)函數(shù)以加載更多數(shù)據(jù),它還提供了一種簡(jiǎn)單的方法來(lái)管理加載和錯(cuò)誤消息的狀態(tài),今天通過(guò)實(shí)例代碼介紹下react滾動(dòng)加載useInfiniteScroll?相關(guān)知識(shí),感興趣的朋友跟隨小編一起看看吧2023-09-09

