กลับไปที่บทความ
React State Management Frontend Architecture

State Management ใน React: สิ่งที่ทำงานจริงในปี 2025

พลากร วรมงคล
10 กันยายน 2567 8 นาที

“ภูมิประเทศ state management ของ React มีความเป็นผู้ใหญ่อย่างมีนัยสำคัญ หลังจากประเมินโซลูชันหลักทั้งหมดข้าม production apps หลายแห่ง นี่คือคู่มือจริงในการเลือกเครื่องมือที่เหมาะสม และทำไมคุณอาจจำเป็นต้องมีน้อยกว่าที่คุณคิด”

The State of State Management

ทุกปี library state management ใหม่สัญญาว่าจะแก้ปัญหา “state problem” ของ React แต่หลังจากสร้าง production apps ด้วย Redux MobX Zustand Jotai Recoil และ React’s built-in tools ฉันสรุปได้ว่าปัญหาจริงไม่ใช่ library มันกำลังวิศวกรรมมากเกินไป state architecture สำหรับ complexity ที่คุณมีจริง ๆ

React applications ส่วนใหญ่ต้องการ state management น้อยกว่าที่ developer คิด ความลับคือการจัดหมวดหมู่ state ของคุณให้ถูกต้องและใช้เครื่องมือที่ง่ายที่สุดสำหรับแต่ละหมวดหมู่

The State Categories

Server State

ข้อมูล fetched จาก API: user profiles product lists order history นี่คือประเภท state ที่พบบ่อยที่สุดในเว็บแอปพลิเคชัน และมีข้อกำหนดเฉพาะ: caching background refetching optimistic updates และ invalidation

ใช้ dedicated server state library: TanStack Query (formerly React Query) หรือ SWR libraries เหล่านี้จัดการ caching deduplication background refetching และ stale-while-revalidate patterns out of the box ก่อน TanStack Query ฉันใช้เวลาหลายสัปดาหในการใช้ logic นี้ใน Redux ตอนนี้ใช้เวลาห้านาที

หากคุณใช้ Next.js กับ Server Components server state management ก็ง่ายยิ่งขึ้น data fetching เกิดขึ้นบนเซิร์ฟเวอร์ และผลลัพธ์จะส่งผ่านเป็น props คุณอาจไม่ต้องการ client-side server state library เลยก็ได้

UI State

State ที่อยู่เฉพาะใน UI: whether a modal is open which tab is selected the current value of a form input whether a sidebar is collapsed State นี้ไม่ persist ไม่ share กับเซิร์ฟเวอร์ และมักจะ scoped ไปยัง single component หรือ small component tree

สำหรับ UI state useState และ useReducer เกือบทั้งหมด sufficient หากคุณต้องการ share UI state ข้าม distant components useContext ทำงานสำหรับ low-frequency updates (theme locale user preferences) อย่าเอาถึง global state library เพียงเพราะว่า components สองตัวต้องการ share boolean

Client State

State ที่อยู่บนไคลเอนต์แต่ไม่เกี่ยวข้องกับ server data: shopping cart contents (ก่อน checkout) form wizard progress draft messages user preferences ที่ยังไม่ได้บันทึก นี่คือพื้นที่สีเทาที่ libraries external state management บางครั้ง make sense

Zustand คือคำแนะนำของฉันสำหรับ client state มันเล็ก (1KB) มี simple API ทำงาน outside ของ React (useful สำหรับ testing) และ scale จาก one store ไปยัง many โดยไม่มี ceremony mental model ชัดเจน: สร้าง store ด้วย state และ actions subscribe ต่อมันจาก components

URL State

State ที่ควร reflected ใน URL: search filters pagination selected tabs sort order state นี้ต้อง survive page refreshes และ shareable ผ่าน links ใช้ URL เองเป็นแหล่งข้อมูล read it ด้วย router’s hooks และ update it ด้วย navigation

นี่คือ state management ที่ developers มักมองข้าม implementing filters ใน useState และหลังจากนั้นประหลาดใจเมื่อ users ไม่สามารถ bookmark หรือ share ฟิลเตอร์ view ของพวกเขา

When You Actually Need Redux

Redux ยังมี legitimate use cases แต่พวกเขาแคบกว่า popularity ของมันแสดงให้เห็น ใช้ Redux เมื่อคุณมี complex state transitions ที่ได้ประโยชน์จาก strict action/reducer pattern คุณต้อง time-travel debugging สำหรับ complex UIs หลาย teams กำลัง contributing ไปยัง same state layer หรือคุณมี significant middleware needs (sagas thunks ด้วย complex flows)

หากคุณเข้าถึง Redux เพราะ “มันคือ standard” pause และ evaluate whether TanStack Query สำหรับ server state บวก Zustand สำหรับ client state จะเป็น simpler ในประสบการณ์ของฉัน combination นี้แทนที่ Redux ใน 80% ของแอปพลิเคชัน ด้วย less code และ less complexity

Patterns That Scale

Colocation

ให้ state เข้ามาใกล้กับจุดที่ใช้ได้มากที่สุด หากเฉพาะ component เดียวต้องการข้อมูล put it ใน component นั้น หากแม่และลูกต้องการมัน lift it ไปยังแม่ hoist state เพียง global store เมื่อมันต้องเป็น global จริง ๆ

หลักการนี้ฟังดูง่าย แต่ต้องมีวินัย誘惑 “just put it ใน global store” นั้นแข็งแกร่ง โดยเฉพาะอย่างยิ่งในทีม used ถึง Redux ต้านทาน global state คือ shared mutable state และทุกชิ้นของ global state เป็นจุดผูก ระหว่าง components

Derived State

อย่าเก็บ state ที่สามารถ compute ได้ หากคุณมี list ของ items และต้องการ filtered count compute count จาก list ไม่ maintain separate counter libraries เช่น Zustand support selectors ที่ derive values จาก store และ React’s useMemo hook จัดการ derived state ภายใน components

การเก็บ derived state เป็น bug factory: ทุกครั้งที่คุณ update source คุณต้อง remember to update derived value ลืมครั้งเดียว และคุณมี inconsistency

State Machines for Complex Flows

Multi-step forms checkout flows และ wizard UIs ได้ประโยชน์จาก explicit state machines libraries เช่น XState model these เป็น finite state machines ด้วย defined states transitions และ guards นี่กำจัด impossible states และทำให้ complex flows predictable และ testable

checkout flow modeled เป็น state machine สามารถไปจาก “cart” ไปยัง “shipping” ไปยัง “payment” ไปยัง “confirmation” เท่านั้น ไม่เคย จาก “cart” directly ไปยัง “confirmation” หรือกลับจาก “confirmation” ไปยัง “payment” โดยไม่มี explicit transition

Performance Considerations

Selective Subscriptions

Global stores cause re-renders ทุกครั้งที่ส่วนใดส่วนหนึ่งของ store เปลี่ยน เว้นแต่คุณใช้ selective subscriptions Zustand’s useStore hook ด้วย selector re-renders เฉพาะเมื่อ selected slice เปลี่ยน TanStack Query re-renders เฉพาะเมื่อ specific query data ที่คุณ subscribe เปลี่ยน

subscribe เสมอไป smallest slice ของ state component ต้องการ component ที่แสดง user’s name ควร subscribe ไปยัง store.user.name ไม่ใช่ entire store

Avoiding Context Pitfalls

React Context re-renders ทุก consumer เมื่อ provider value เปลี่ยน หากค่า context ของคุณเป็น object ที่ recreated ในทุก render ทุก consumer re-renders ในทุก render แม้ว่า specific value พวกเขา care ไม่เปลี่ยน

แบ่ง large contexts เป็น smaller focused contexts (one สำหรับ theme one สำหรับ user one สำหรับ feature flags) หรือใช้ state management library แทน พวกเขา handle granular subscriptions natively

My Standard Stack

สำหรับ new React projects default state management stack ของฉันคือ: TanStack Query สำหรับ server state useState และ useReducer สำหรับ component-local UI state Zustand สำหรับ shared client state (หากจำเป็น) และ URL params สำหรับ filterable หรือ shareable state นี้ครอบคลุมทุก real-world need ที่ฉันเจอ ด้วย minimal boilerplate และ excellent developer experience

Comments powered by Giscus are not yet configured. Set PUBLIC_GISCUS_REPO_ID and PUBLIC_GISCUS_CATEGORY_ID in apps/web/.env to enable.

PV

เขียนโดย พลากร วรมงคล

Software Engineer Specialist ประสบการณ์กว่า 20 ปี เขียนเกี่ยวกับ Architecture, Performance และการสร้างระบบ Production

เพิ่มเติมเกี่ยวกับผม

บทความที่เกี่ยวข้อง