การจัดการของรัฐถือเป็นหัวใจสำคัญของทุกแอปพลิเคชัน React ที่ไม่สำคัญ ทำให้ถูกต้องและโค้ดเบสของคุณจะปรับขนาดได้อย่างสวยงาม หากทำผิดและคุณจะพบกับฝันร้ายที่เจาะทะลุอุปกรณ์ UI ที่เก่า และส่วนประกอบต่างๆ ที่ถูกลืมเลือนอีกครั้ง ข่าวดีก็คือว่าระบบนิเวศ React ได้เติบโตอย่างรวดเร็ว ตอนนี้คุณมีเมนูเครื่องมือที่สร้างขึ้นตามวัตถุประสงค์มากมาย เช่น Redux Toolkit, Zustand, Jotai, TanStack Query และ Context API ในตัว ซึ่งแต่ละเมนูได้รับการปรับให้เหมาะกับระดับปัญหาเฉพาะ
บทความนี้จะอธิบายวิธีแก้ปัญหาแต่ละอย่างในเชิงลึก อธิบายข้อดีข้อเสียด้วยตัวอย่างที่เป็นรูปธรรม และให้กรอบการตัดสินใจที่คุณสามารถนำไปใช้กับโครงการของคุณได้ทันที
ทำความเข้าใจกับรัฐประเภทต่างๆ
ก่อนที่จะเข้าถึงห้องสมุดใดๆ คุณควรระบุให้ชัดเจนว่าคุณกำลังจัดการสถานะประเภทใด การรวมหมวดหมู่สถานะที่แตกต่างกันเป็นสาเหตุหลักของแอปพลิเคชัน React ที่ได้รับการออกแบบมากเกินไป
- สถานะ UI ท้องถิ่น — ไม่ว่าดรอปดาวน์เปิดอยู่ แท็บใดที่ใช้งานอยู่ ค่าปัจจุบันของอินพุตที่ควบคุม สถานะนี้เป็นของคอมโพเนนต์เดียวหรือทรีย่อยขนาดเล็ก
- สถานะไคลเอ็นต์ที่ใช้ร่วมกัน — ข้อมูลที่องค์ประกอบหลายรายการที่อาจอยู่ห่างไกลจำเป็นต้องอ่านหรือเขียน: ผู้ใช้ที่ได้รับการตรวจสอบสิทธิ์ในปัจจุบัน การตั้งค่าธีม ตะกร้าสินค้า
- สถานะเซิร์ฟเวอร์ — ข้อมูลที่เริ่มต้นบนเซิร์ฟเวอร์ มีลักษณะไม่พร้อมกัน และจำเป็นต้องมีการตรวจสอบแคช การดึงข้อมูลพื้นหลังอีกครั้ง และการจัดการวงจรการโหลด/ข้อผิดพลาด
- สถานะ URL — ตัวกรอง การแบ่งหน้า และคำค้นหาที่ควรรีเฟรชหน้าและแชร์ผ่านลิงก์ได้
- สถานะแบบฟอร์ม — อินพุตที่ควบคุม ข้อผิดพลาดในการตรวจสอบ และสถานะการส่ง มักจะได้รับการจัดการที่ดีที่สุดโดยไลบรารีเฉพาะ เช่น React Hook Form
สิ่งเดียวที่มีผลกระทบมากที่สุดที่คุณสามารถทำได้คือต่อต้านความอยากที่จะเทรัฐทุกประเภทมาไว้ในร้านค้าระดับโลกแห่งเดียว แต่ละหมวดหมู่มีข้อกำหนดด้านความสอดคล้องที่แตกต่างกัน อายุการใช้งานที่แตกต่างกัน และความถี่ในการอัปเดตที่แตกต่างกัน
บริบท React: ตัวเลือกในตัว
บริบท API มาพร้อมกับ React และไม่จำเป็นต้องมีการขึ้นต่อกันเพิ่มเติม มันทำงานได้ดีสำหรับสถานะที่เปลี่ยนแปลงไม่บ่อยนักและถูกใช้โดยคอมโพเนนต์จำนวนมาก ตัวอย่างคลาสสิก ได้แก่ ธีม สถานที่ และอ็อบเจ็กต์ผู้ใช้ที่ได้รับการตรวจสอบสิทธิ์
// AuthContext.tsx
import { createContext, useContext, useState, ReactNode } from 'react';
interface AuthState {
user: User | null;
login: (credentials: Credentials) => Promise<void>;
logout: () => void;
}
const AuthContext = createContext<AuthState | null>(null);
export function AuthProvider({ children }: { children: ReactNode }) {
const [user, setUser] = useState<User | null>(null);
const login = async (credentials: Credentials) => {
const user = await authService.login(credentials);
setUser(user);
};
const logout = () => setUser(null);
return (
<AuthContext.Provider value={{ user, login, logout }}>
{children}
</AuthContext.Provider>
);
}
export function useAuth() {
const ctx = useContext(AuthContext);
if (!ctx) throw new Error('useAuth must be used inside AuthProvider');
return ctx;
}
กับดักประสิทธิภาพบริบท
บริบทมีลักษณะการทำงานที่รู้จักกันดีซึ่งทำให้นักพัฒนาจำนวนมากต้องสะดุด กล่าวคือ ผู้บริโภคทุกรายจะแสดงผลซ้ำทุกครั้งที่การอ้างอิงค่าบริบทเปลี่ยนแปลง หากคุณจัดเก็บออบเจ็กต์ขนาดใหญ่และกลายพันธุ์บ่อยครั้งไว้ในบริบทเดียว คุณจะทริกเกอร์การเรนเดอร์ซ้ำโดยไม่จำเป็นทั่วทั้งแผนผังของคุณ
กลยุทธ์ในการลดผลกระทบ ได้แก่ :
- แบ่งบริบทตามความถี่ในการอัปเดต เก็บออบเจ็กต์ผู้ใช้ไว้ในบริบทหนึ่งและการตั้งค่า UI ไว้ในบริบทอื่น
- จดจำวัตถุค่าด้วย
useMemoดังนั้นการอ้างอิงจะเปลี่ยนแปลงเมื่อข้อมูลเปลี่ยนแปลงจริงเท่านั้น - ใช้
React.memoบนส่วนประกอบของผู้บริโภคเพื่อข้ามการเรนเดอร์ซ้ำเมื่ออุปกรณ์ประกอบฉากที่พวกเขาสนใจไม่มีการเปลี่ยนแปลง
ข้อสรุปที่ตรงไปตรงมาก็คือบริบทนั้นยอดเยี่ยมสำหรับสถานะทั่วโลกที่มีความถี่ต่ำ แต่ไม่ใช่โซลูชันการจัดการสถานะที่มีจุดประสงค์ทั่วไป เมื่อคุณพบว่าตัวเองกำลังเขียนบันทึกช่วยจำที่ซับซ้อนเพื่อแก้ไขพฤติกรรมการเรนเดอร์ซ้ำของ Context ก็ถึงเวลาที่จะต้องเข้าถึงห้องสมุดเฉพาะ
ชุดเครื่องมือ Redux: ตัวเลือกระดับองค์กรที่สมบูรณ์
Redux มีชื่อเสียงในด้านสำเร็จรูป — ชื่อเสียงที่ได้รับในยุคก่อนชุดเครื่องมือ Redux Toolkit (RTK) กำจัดพิธีการในขณะที่ยังคงรักษาสิ่งที่ทำให้ Redux มีประสิทธิภาพ: แผนผังสถานะเดียวที่คาดเดาได้และตรวจสอบได้พร้อมกระแสข้อมูลทิศทางเดียวที่เข้มงวด
เรือ RTK createSliceซึ่งสร้างผู้สร้างการกระทำและตัวลดจากคำจำกัดความเดียว และใช้ Immer ภายใต้ประทุนเพื่อให้คุณสามารถเขียนการกลายพันธุ์ที่ดูจำเป็น แต่นำไปใช้จริงอย่างไม่เปลี่ยนรูป
// features/cart/cartSlice.ts
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
interface CartItem {
id: string;
name: string;
quantity: number;
price: number;
}
interface CartState {
items: CartItem[];
coupon: string | null;
}
const initialState: CartState = { items: [], coupon: null };
export const cartSlice = createSlice({
name: 'cart',
initialState,
reducers: {
addItem(state, action: PayloadAction<CartItem>) {
const existing = state.items.find(i => i.id === action.payload.id);
if (existing) {
existing.quantity += action.payload.quantity;
} else {
state.items.push(action.payload);
}
},
removeItem(state, action: PayloadAction<string>) {
state.items = state.items.filter(i => i.id !== action.payload);
},
applyCoupon(state, action: PayloadAction<string>) {
state.coupon = action.payload;
},
},
});
export const { addItem, removeItem, applyCoupon } = cartSlice.actions;
export default cartSlice.reducer;
แบบสอบถาม RTK: สถานะเซิร์ฟเวอร์ภายใน Redux
RTK จัดส่งคู่หูที่เรียกว่า RTK Query ซึ่งจัดการสถานะเซิร์ฟเวอร์โดยตรงภายในร้าน Redux โดยจะสร้างตะขอ React โดยอัตโนมัติจากคำจำกัดความปลายทางและจัดการแคช การทำให้ใช้งานไม่ได้ และการอัปเดตในแง่ดี
// services/productsApi.ts
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
export const productsApi = createApi({
reducerPath: 'productsApi',
baseQuery: fetchBaseQuery({ baseUrl: '/api' }),
tagTypes: ['Product'],
endpoints: (builder) => ({
getProducts: builder.query<Product[], void>({
query: () => '/products',
providesTags: ['Product'],
}),
updateProduct: builder.mutation<Product, Partial<Product>>({
query: ({ id, ...patch }) => ({
url: `/products/${id}`,
method: 'PATCH',
body: patch,
}),
invalidatesTags: ['Product'],
}),
}),
});
export const { useGetProductsQuery, useUpdateProductMutation } = productsApi;
Redux Toolkit เป็นตัวเลือกที่เหมาะสมเมื่อคุณต้องการการดีบักการเดินทางข้ามเวลาผ่าน Redux DevTools เมื่อหลายทีมจำเป็นต้องสนับสนุนโมเดลสถานะที่ใช้ร่วมกันพร้อมแบบแผนที่บังคับใช้ หรือเมื่อคุณมีตรรกะทางธุรกิจแบบ cross-slice ที่ซับซ้อนซึ่งได้รับประโยชน์จากมิดเดิลแวร์ เช่น redux-saga หรือ redux-observable.
Zustand: รัฐระดับโลกรุ่นไลท์เวทโดยไม่ต้องมีพิธีการ
Zustand ใช้แนวทางที่แตกต่างอย่างสิ้นเชิง ไม่มีผู้ให้บริการ ไม่มีตัวลด และไม่มีประเภทการดำเนินการ คุณกำหนดร้านค้าเป็นอ็อบเจ็กต์ JavaScript ธรรมดาด้วยสถานะและวิธีการ จากนั้นใช้งานด้วยฮุกเดียว API ทั้งหมดพอดีบนหน้าจอเดียว
// stores/useCartStore.ts
import { create } from 'zustand';
import { persist, devtools } from 'zustand/middleware';
interface CartStore {
items: CartItem[];
addItem: (item: CartItem) => void;
removeItem: (id: string) => void;
totalPrice: () => number;
clear: () => void;
}
export const useCartStore = create<CartStore>()(
devtools(
persist(
(set, get) => ({
items: [],
addItem: (item) =>
set((state) => {
const existing = state.items.find((i) => i.id === item.id);
if (existing) {
return {
items: state.items.map((i) =>
i.id === item.id
? { ...i, quantity: i.quantity + item.quantity }
: i
),
};
}
return { items: [...state.items, item] };
}),
removeItem: (id) =>
set((state) => ({ items: state.items.filter((i) => i.id !== id) })),
totalPrice: () =>
get().items.reduce((sum, i) => sum + i.price * i.quantity, 0),
clear: () => set({ items: [] }),
}),
{ name: 'cart-storage' }
)
)
);
การสมัครสมาชิกและประสิทธิภาพโดยละเอียด
Zustand แก้ปัญหาการเรนเดอร์ซ้ำของ Context ผ่านการสมัครสมาชิกตามตัวเลือก ส่วนประกอบจะแสดงผลซ้ำเมื่อส่วนของสถานะที่เลือกมีการเปลี่ยนแปลงเท่านั้น
// Only re-renders when items.length changes, not on price changes
const itemCount = useCartStore((state) => state.items.length);
// Only re-renders when the total changes
const total = useCartStore((state) => state.totalPrice());
ระบบนิเวศมิดเดิลแวร์ของ Zustand ครอบคลุมถึงความคงอยู่ localStorage, การรวม DevTools, การเปลี่ยนรูปแบบ Immer และการซิงค์ URL เป็นตัวเลือกที่เหมาะสำหรับทีมที่ต้องการความสามารถระดับ Redux โดยไม่ต้องเสียค่าติดตั้งระดับ Redux
Jotai: สถานะปรมาณูได้รับแรงบันดาลใจจากการหดตัว
แบบจำลอง Jotai ระบุเป็นกราฟของอะตอม ซึ่งเป็นหน่วยสถานะขนาดเล็กที่สามารถประกอบขึ้นได้ซึ่งสามารถหาได้จากกันและกัน โมเดลนี้มีประสิทธิภาพเป็นพิเศษสำหรับสถานะไดนามิก ลองนึกถึงรายการโหนดตัวแก้ไขโดยที่แต่ละโหนดมีสถานะการเลือก/โฟกัสที่เป็นอิสระของตัวเอง หรือรูปแบบที่ซับซ้อนซึ่งการมองเห็นฟิลด์ขึ้นอยู่กับค่าฟิลด์อื่น ๆ
// atoms/filterAtoms.ts
import { atom, selector } from 'jotai';
export const searchTermAtom = atom('');
export const categoryAtom = atom<string | null>(null);
export const productsAtom = atom<Product[]>([]);
// Derived atom — recomputes only when its dependencies change
export const filteredProductsAtom = atom((get) => {
const products = get(productsAtom);
const term = get(searchTermAtom).toLowerCase();
const category = get(categoryAtom);
return products.filter((p) => {
const matchesTerm = p.name.toLowerCase().includes(term);
const matchesCategory = category === null || p.category === category;
return matchesTerm && matchesCategory;
});
});
// ProductList.tsx
import { useAtom, useAtomValue } from 'jotai';
function ProductList() {
const [searchTerm, setSearchTerm] = useAtom(searchTermAtom);
const filtered = useAtomValue(filteredProductsAtom);
return (
<>
<input value={searchTerm} onChange={(e) => setSearchTerm(e.target.value)} />
{filtered.map((p) => <ProductCard key={p.id} product={p} />)}
</>
);
}
แบบจำลองปฏิกิริยาที่ละเอียดของ Jotai หมายความว่าเมื่อคำค้นหาเปลี่ยนแปลง จะมีเฉพาะส่วนประกอบที่สมัครเป็นสมาชิกเท่านั้น filteredProductsAtom แสดงผลอีกครั้ง ส่วนประกอบที่สมัครสมาชิกเท่านั้น categoryAtom ไม่ได้ถูกแตะต้อง สิ่งนี้ทำให้ Jotai เป็นเลิศสำหรับการใช้งานกับกราฟสถานะที่หนาแน่นและพึ่งพาซึ่งกันและกัน
TanStack Query: หน้าแรกที่เหมาะสมสำหรับสถานะเซิร์ฟเวอร์
TanStack Query (เดิมชื่อ React Query) ไม่ใช่ไลบรารีการจัดการสถานะทั่วไป แต่เป็นไลบรารีสถานะเซิร์ฟเวอร์และจัดการความรับผิดชอบนั้นได้ดีกว่าสิ่งอื่นใดในระบบนิเวศ โดยทั่วไปแล้ว การดึงข้อมูล การแคช การซิงโครไนซ์ และการอัปเดตข้อมูลระยะไกลใน React โดยไม่มี TanStack Query หมายถึงการจัดการการโหลดแฟล็ก อ็อบเจ็กต์ข้อผิดพลาด และการทำให้แคชใช้ไม่ได้ด้วยตนเองในสถานะท้องถิ่นหรือร้านค้าส่วนกลาง TanStack Query ทำทุกอย่างโดยอัตโนมัติ
// hooks/useProducts.ts
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
export function useProducts(filters: ProductFilters) {
return useQuery({
queryKey: ['products', filters],
queryFn: () => api.getProducts(filters),
staleTime: 5 * 60 * 1000, // treat data as fresh for 5 minutes
gcTime: 10 * 60 * 1000, // keep unused data in cache for 10 minutes
});
}
export function useUpdateProduct() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (update: ProductUpdate) => api.updateProduct(update),
// Optimistic update
onMutate: async (update) => {
await queryClient.cancelQueries({ queryKey: ['products'] });
const previous = queryClient.getQueryData(['products']);
queryClient.setQueryData(['products'], (old: Product[]) =>
old.map((p) => (p.id === update.id ? { ...p, ...update } : p))
);
return { previous };
},
onError: (_err, _update, context) => {
queryClient.setQueryData(['products'], context?.previous);
},
onSettled: () => {
queryClient.invalidateQueries({ queryKey: ['products'] });
},
});
}
อาร์เรย์คีย์แบบสอบถามคือการแคชแบบดั้งเดิมของ TanStack Query แบบสอบถามใด ๆ ที่ใช้คีย์เดียวกันร่วมกันจะใช้รายการแคชเดียวกัน เมื่อคุณทำให้คีย์ใช้ไม่ได้ ทุกองค์ประกอบที่สมัครรับคีย์นั้นจะถูกดึงข้อมูลอีกครั้งโดยอัตโนมัติในเบื้องหลังและอัปเดตเมื่อมีข้อมูลใหม่เข้ามา
การดึงข้อมูลล่วงหน้าและไฮเดรชั่นใน Next.js
ในแอปพลิเคชัน Next.js คุณสามารถดึงข้อมูลข้อความค้นหาล่วงหน้าบนเซิร์ฟเวอร์และแยกน้ำออกจากข้อมูล HTML เพย์โหลด ซึ่งช่วยลดการโหลดสปินเนอร์สำหรับการแสดงผลหน้าแรกโดยสิ้นเชิง
// app/products/page.tsx (Next.js App Router)
import { dehydrate, HydrationBoundary, QueryClient } from '@tanstack/react-query';
export default async function ProductsPage() {
const queryClient = new QueryClient();
await queryClient.prefetchQuery({
queryKey: ['products', {}],
queryFn: () => api.getProducts({}),
});
return (
<HydrationBoundary state={dehydrate(queryClient)}>
<ProductList />
</HydrationBoundary>
);
}
การรวมห้องสมุด: สถาปัตยกรรมเชิงปฏิบัติ
การใช้งานจริงมักจะได้ประโยชน์จากการรวมเครื่องมือหลายอย่างเข้าด้วยกัน โดยแต่ละเครื่องมือจะรับผิดชอบตามหมวดหมู่สถานะของตนเอง แอปพลิเคชัน React ขนาดกลางถึงใหญ่ที่มีโครงสร้างดีอาจมีลักษณะดังนี้:
- แบบสอบถาม TanStack เป็นเจ้าของสถานะเซิร์ฟเวอร์ทั้งหมด มันดึงข้อมูลแคชและซิงโครไนซ์ข้อมูลระยะไกล ไม่มีสิ่งอื่นใดที่แตะการตอบสนอง API
- ซูสแตนด์ เป็นเจ้าของสถานะไคลเอ็นต์ทั่วโลก เช่น ตะกร้าสินค้า เซสชันผู้ใช้ที่ได้รับการตรวจสอบสิทธิ์ การตั้งค่า UI และการประสานงานข้ามคุณลักษณะใดๆ ที่ซับซ้อนเกินไปสำหรับบริบท
- บริบท React เป็นเจ้าของการกำหนดค่าแบบ low-churn — ธีมปัจจุบัน, สถานที่, แฟล็กคุณลักษณะ
- useState / useReducer สถานะของคอมโพเนนต์ในเครื่องของตัวเอง — โมดอลเปิด/ปิด, ค่าฟิลด์ของฟอร์ม (หรือ React Hook Form สำหรับฟอร์มที่ซับซ้อน), การเลือกแท็บ
- พารามิเตอร์การค้นหา URL สถานะการนำทางของตัวเอง — ตัวกรองที่ใช้งานอยู่ ลำดับการจัดเรียง หมายเลขหน้าปัจจุบัน
การแยกข้อกังวลนี้ไม่ใช่แค่ความสวยงามเท่านั้น หมายความว่าแคชสถานะเซิร์ฟเวอร์ของคุณจะไม่ถูกรบกวนด้วยสถานะ UI ร้านค้าไคลเอนต์ทั่วโลกของคุณจะไม่เต็มไปด้วยข้อมูลการตอบสนองที่ TanStack Query จะแคชได้อย่างมีประสิทธิภาพมากขึ้น และบริบทของคุณจะไม่ทริกเกอร์การเรนเดอร์ซ้ำทั้งแอปเนื่องจากมีผู้เก็บค่าที่เปลี่ยนแปลงอย่างรวดเร็วไว้ในนั้น
รูปแบบประสิทธิภาพที่ควรรู้
ตัวเลือกที่บันทึกไว้พร้อมกับการเลือกใหม่
เมื่อรับค่าที่คำนวณจากร้านค้า Redux ให้ใช้การเลือกใหม่เพื่อหลีกเลี่ยงการคำนวณใหม่ในการเรนเดอร์ทุกครั้ง RTK ส่งออกซ้ำ createSelector จากการเลือกใหม่
import { createSelector } from '@reduxjs/toolkit';
const selectItems = (state: RootState) => state.cart.items;
export const selectCartSummary = createSelector(selectItems, (items) => ({
count: items.reduce((n, i) => n + i.quantity, 0),
total: items.reduce((sum, i) => sum + i.price * i.quantity, 0),
}));
การแยกการเรนเดอร์ด้วย useTransition
React18's useTransition ให้คุณทำเครื่องหมายการอัปเดตสถานะว่าไม่เร่งด่วน เพื่อให้เบราว์เซอร์สามารถขัดจังหวะการอัปเดตเพื่อจัดการงานที่มีลำดับความสำคัญสูงกว่า เช่น อินพุตของผู้ใช้ สิ่งนี้มีประโยชน์อย่างยิ่งเมื่อการเปลี่ยนแปลงสถานะทำให้เกิดการเรนเดอร์ซ้ำซึ่งมีราคาแพง
const [isPending, startTransition] = useTransition();
const handleFilterChange = (value: string) => {
startTransition(() => {
setFilterValue(value); // expensive downstream re-render is deferrable
});
};
หลีกเลี่ยงปัญหาเอกลักษณ์ของวัตถุ
แหล่งที่มาทั่วไปของการเรนเดอร์ Phantom อีกครั้งคือการสร้างออบเจ็กต์ใหม่หรือตัวอักษรอาร์เรย์ภายในการเรนเดอร์ useMemo and useCallback เป็นเครื่องมือของคุณที่นี่ แต่นำไปใช้เฉพาะเมื่อคุณมีหลักฐานของปัญหาด้านประสิทธิภาพเท่านั้น การจดบันทึกก่อนเวลาอันควรจะเพิ่มค่าใช้จ่ายด้านการรับรู้โดยไม่รับประกันผลประโยชน์
กรอบการตัดสินใจ
ใช้แผนผังการตัดสินใจต่อไปนี้เมื่อเลือกแนวทางการจัดการสถานะสำหรับคุณลักษณะใหม่:
- รัฐเป็นสถานะเฉพาะขององค์ประกอบเดียวหรือทรีย่อยขนาดเล็กหรือไม่ ใช้
useStateหรือuseReducer. - รัฐแสดงถึงข้อมูลที่ดึงมาจากเซิร์ฟเวอร์หรือไม่ ใช้ แบบสอบถาม TanStack.
- ลูกค้าทั่วโลกระบุว่ามีส่วนประกอบจำนวนมากในแผนผังต้องการหรือไม่ เข้าถึงเพื่อ ซูสแตนด์ เว้นแต่คุณจะอยู่ในโค้ดเบส Redux อยู่แล้ว
- สถานะเปลี่ยนแปลงน้อยครั้งและตอบสนองวัตถุประสงค์ในการกำหนดค่า (ธีม สถานที่) หรือไม่ ใช้ บริบท React.
- รัฐมีความเชื่อมโยงถึงกันอย่างมากกับคุณค่าที่ได้รับมามากมายหรือไม่? พิจารณา โจไท.
- คุณต้องการการแก้ไขข้อบกพร่องในการเดินทางข้ามเวลา โครงสร้างทางสถาปัตยกรรมที่เข้มงวด หรือเรื่องราวเกี่ยวกับอะซิงก์ที่ซับซ้อนหรือไม่? ใช้ ชุดเครื่องมือ Redux.
รูปแบบการต่อต้านทั่วไปที่ควรหลีกเลี่ยง
- การจัดเก็บการตอบสนองของเซิร์ฟเวอร์ใน Redux หรือ Zustand ให้ TanStack Query เป็นเจ้าของข้อมูลนั้น การทำซ้ำในร้านค้าส่วนกลางจะสร้างข้อบกพร่องในการซิงโครไนซ์
- รวบรวมทุกสิ่งไว้ในร้านค้าขนาดใหญ่เพียงแห่งเดียว สถานะที่ไม่เกี่ยวข้องซึ่งอยู่ในสไลซ์เดียวกันบังคับให้มีการมีเพศสัมพันธ์โดยไม่จำเป็น และทำให้การทดสอบยากขึ้น
- การใช้บริบทสำหรับการอัพเดตความถี่สูง การเปลี่ยนแปลงค่าบริบททุกครั้งจะแสดงผลผู้บริโภคทั้งหมดอีกครั้ง สำหรับค่าที่เปลี่ยนแปลงในทุกการกดแป้นพิมพ์หรือเฟรมภาพเคลื่อนไหว บริบทเป็นเครื่องมือที่ไม่ถูกต้อง
- ได้รับสถานะภายในส่วนประกอบ ค่าที่คำนวณซึ่งขึ้นอยู่กับสถานะควรเป็นตัวเลือกบันทึกหรืออะตอมที่ได้รับ ไม่ใช่การคำนวณแบบอินไลน์ที่ทำงานในทุกการเรนเดอร์
- ละเว้นสถานะการโหลดและข้อผิดพลาด สถานะของเซิร์ฟเวอร์เป็นแบบอะซิงโครนัสโดยเนื้อแท้ พื้นผิวแบบสอบถาม TanStack
isLoading,isErrorและerrorสำหรับทุกคำถาม ใช้พวกเขา
บทสรุป
ภูมิทัศน์การจัดการของรัฐใน React ไม่เคยดีเท่านี้มาก่อน คุณไม่จำเป็นต้องเลือกระหว่างเฟรมเวิร์กที่หนักหน่วงกับการสร้างทุกอย่างตั้งแต่เริ่มต้นอีกต่อไป Redux Toolkit มอบความสามารถในการคาดการณ์ระดับองค์กรโดยไม่ต้องมีต้นแบบเหมือนรุ่นก่อน Zustand มอบพลังแบบเดียวกันนั้นด้วยการตั้งค่าเพียงเล็กน้อย Jotai แก้ปัญหายากๆ ด้วยกราฟสถานะไดนามิกที่มีรายละเอียดละเอียด และ TanStack Query ได้แก้ไขสถานะเซิร์ฟเวอร์อย่างแน่นอน — หากคุณยังคงจัดการการตอบสนอง API ใน useEffect and useStateคุณเป็นหนี้ตัวเองในการย้ายถิ่นฐาน
ข้อมูลเชิงลึกที่สำคัญที่สุดคือการจัดหมวดหมู่: ระบุสถานะที่คุณกำลังเผชิญอยู่ก่อน จากนั้นเลือกเครื่องมือที่ปรับให้เหมาะกับหมวดหมู่นั้น ต่อต้านสัญชาตญาณที่จะรวมทุกอย่างไว้ในระบบเดียว แนวทางแบบหลายชั้น — TanStack Query สำหรับสถานะเซิร์ฟเวอร์, Zustand สำหรับสถานะไคลเอนต์ทั่วโลก, บริบทสำหรับการกำหนดค่า และสถานะท้องถิ่นสำหรับทุกสิ่ง — สร้างฐานโค้ดที่ง่ายต่อการให้เหตุผล ทดสอบได้ง่ายขึ้น และบำรุงรักษาได้มากขึ้นเมื่อทีมและความต้องการเติบโตขึ้น