Top view of Redux

Naravit bunthap
odds.team
Published in
8 min readJan 3, 2024

สวัสดีผู้อ่านทุกคนครับ ในบทความนี้เราจะมาทำความเข้าใจสิ่งที่เรียกว่า Redux โดยอิงจาก React framework กันนะครับ โดยเนื้อหาที่เราจะพูดคุยในบทความนี้ก็ตามด้านล่างนี้เลย 🤩

Topics

  • ขอออกตัวก่อนด้วย disclaimer
  • ทำไม redux ถึงเกิดขึ้นมา ?
  • redux คืออะไรกัน ?
  • ภาพรวมของ redux
  • มาลองสร้าง app ง่ายๆโดยใช้ redux กัน
  • ข้อดี ข้อเสีย ของ redux

disclaimer

เมื่อไม่นานมานี้ ผมได้มีโอกาสไปทำงานใน legacy project ที่ใช้ redux แต่ผมไม่เคยใช้งานมันมาก่อน จึงได้เริ่มศึกษาและทำความเข้าใจ รวมทั้งสอบถามผู้รู้หลายคน เมื่อเริ่มเข้าใจมากขึ้น จึงอยากนำความรู้นี้มาบันทึก และแบ่งปันให้ผู้เริ่มต้นคนอื่นๆ หากมีเนื้อหาส่วนไหนที่ผิดพลาด สามารถติชมหรือบอกกล่าวกันมาได้เลยนะครับ ขอบคุณครับ 😃🙏

ทำไม redux ถึงเกิดขึ้นมา ?

เมื่อเราเริ่มเขียน App ขึ้นมา ในขณะที่ App ของเราเพิ่งเริ่มต้น และมีขนาดเล็กอยู่ การบริหารจัดการ state และ props ที่จะส่งต่อๆกันในแต่ละ component ก็ยังไม่เป็นปัญหามากนัก แต่เมื่อ App ของเรามีขนาดใหญ่ขึ้น สิ่งที่เพิ่มตามขึ้นมาคือจำนวนของ component และจำนวนชั้นของ component ที่ “ ลึกมากขึ้น

รูปที่ 1 ภาพจำลอง component level ที่เยอะขึ้นเมื่อแอปมีขนาดใหญ่ขึ้น

จากรูป หากเราอยากเปลี่ยนแปลงค่าบางอย่างจาก child 4 ไปแสดงที่ child 6 เราจะทำยังไงดี

  1. ก็ง่ายๆ เก็บ stage ไว้ที่ App เลย
    - โยน function setState ผ่าน props ของ child 1 -> ส่งต่อไปผ่าน props ของ child 4
    - ที่นี้พอมีการเปลี่ยนแปลงของ state ใน child 4 ค่า state ที่เก็บใน App จะถูกเปลี่ยน
    - ค่าจะถูกส่งผ่าน props ของ child 2 -> ส่งต่อไปผ่าน props ของ child 6 เพื่อนำไปแสดงผล (วุ่นวายสุดๆ 🤯)
รูปที่ 2 ภาพจำลองการส่งค่าผ่าน props ของแต่ละ component โดยอิงจาก dom tree ในรูป 1

ซึ่งจะเห็นว่า child 1และ child 2 เป็นเพียงทางผ่านของค่าต่างๆเท่านั้น ไม่ได้นำค่าเหล่านั้นมาใช้เลย แค่รับมาและส่งต่อไป ทำให้เกิด code ที่ไม่จำเป็นมากขึ้น

2. ใช้ “redux” สิ แค่จัดการ “เชื่อม” child 4 และ child 6 เข้ากับ redux เพียงเท่านี้เรา ก็สามารถส่งค่าไปมาระหว่าง 2 component ได้แล้ว (ง่ายเกิ้นนน 😎)

จะเห็นว่าการใช้ redux ดูง่ายเหมือนใช้เวทมนต์กันเลยทีเดียว ถ้าอย่างนั้นเราไปทำความรู้จักมันเพิ่มกันดีกว่า (เพื่อความสบายในชีวิตของเรา 😆)

Redux คืออะไรกัน?

Redux คือ state management ที่จะคอยช่วยบริหาร state ของเราจากภายในตัว redux เองทำให้เราไม่จำเป็นต้องส่งค่าของ state ต่างๆผ่าน component ที่ไม่จำเป็นต้องใช้ state เหล่านั้นอีกต่อไป โดยมีคุณสมบัติที่น่าสนใจอยู่อย่างเช่น

  1. single source of trust คือ ความเชื่อมั่นว่า ค่าของ state จะมีอยู่แค่ที่เดียว และไม่มีค่านี้ซ้ำที่ไหนหรือสามารถเปลี่ยนแปลงได้ที่ไหนอีก เพราะเราเก็บ state ของทั้ง App ไว้ที่เดียวใน redux (อธิบายเพิ่มเติมในหัวข้อต่อไป)
  2. state can only read คือ ไม่ได้หมายความว่า ค่าใน state จะเปลี่ยนแปลงไม่ได้ แต่หมายถึงถ้าจะเปลี่ยนแปลงมันต้องเปลี่ยนผ่านการใช้ dispatch function เท่านั้น (อธิบายเพิ่มในหัวข้อต่อไป) จึงทำให้เรามั่นใจว่าค่าของ state ของทั้ง App จะไม่เปลี่ยนแปลงเองขณะเราเรียกใช้
  3. state can change with pure function คือ ใน redux เวลาเราเปลี่ยนแปลง state ภายในตัว redux จะทำการ operate การเปลี่ยนแปลงด้วย pure function เสมอซึ่งทำให้เรามั่นใจได้ว่าการเปลี่ยนแปลงนั้นจะไม่เกิด side effect ที่ไม่พึงประสงค์

ภาพรวมของ redux

ผมเองเป็นคนที่ชอบเห็นภาพรวมกว้างๆของสิ่งที่จะเรียนรู้ก่อนถ้าเป็นไปได้ เพราะงั้นก่อนที่เราจะดำดิ่งลงไปใน redux เราลองถอยมาดูภาพรวมของ redux กันก่อนดีกว่า เพื่อทำความเข้าใจการทำงาน และหน้าที่ของส่วนต่างๆ รวมถึงเข้าใจวงจรของ redux มากขึ้น

รูปที่ 3 ภาพรวมของ redux

หลังจากเห็นภาพกันแล้ว เรามาลองทำความเข้าใจทีละส่วนกัน เริ่มจาก

Redux
จากที่เราได้พูดถึง redux ช่วงแรกๆของบทความนี้ ก็อาจทำให้ทุกคนพอเห็นภาพของมันมากขึ้น ว่ามันคือพื้นที่อีกพื้นที่นึงใน App ของเราที่คอยช่วยเก็บ state และจัดการเปลี่ยนแปลงค่าของ state ตามที่เราต้องการรวมถึงการส่งต่อ ค่าของ state และ function สำหรับเปลี่ยนแปลง state ให้กับ component ที่ต้องการใช้อีกด้วย ซึ่งข้างในพื้นที่นี้จะประกอบไปด้วย

  • Store ศูนย์กลางของ redux ที่ทำหน้าที่เก็บ state ของทั้ง App ไว้และส่งผ่านเข้าไปใน App เพื่อรอ component เรียกใช้
    - โดย store จะถูกสร้างขึ้นมาเพียง 1 อันใน 1 App และถูกสร้างขึ้นโดย createStore()
    - createStore() จะรับ parameter 3 ตัว
    1. Reducer ซึ่งใน 1 App อาจมีมากกว่า 1 ตัวได้ ถ้ามีมากกว่าหนึ่งตัวต้องนำเข้า combineReducer() ก่อนนำไปใส่ใน createStroe()
    2. InitialState (optional) คือ ค่าเริ่มต้นของ state ใน App
    3. applyMiddleware (optional) คือ function สำหรับใส่ middleware ต่างๆเข้าไปใน redux เช่น logger, thunk (ซึ่งยังไม่ได้กล่าวถึงในบทความนี้)
import { createStore } from 'redux'
import valueReducer from './redux/value/valueReducer'
impoer otherReducer from './redeux/other/otherReducer'

const appReducer = combineReducer({valueReducer, otherReducer})

const store = createStore(appReducer, initialState, applyMiddleware());
// parameter 2,3 of createStore are optional
  • Reducer ส่วนที่คอยควบคุมการเปลี่ยนแปลงค่าของ state โดย reducer จะรอเมื่อมี action เกิดขึ้น ก็จะรับหน้าที่เปลี่ยน state ตาม type ของ action นั้นๆที่รับเข้ามา
    โดยรูปแบบการเขียน reducer มักเขียนในรูปแบบของ function ที่มี switch case และมีการ return จะออกมาเป็น new state object เพื่อนำไปแทนที่ state เก่า เพื่อไม่ให้เป็นการ mutate ค่าของ state ตามคุณสมบัติของ redux ข้อ 3ในหัวข้อที่ผ่านมา (state can change with pure function) เราลองมาดูตัวอย่าง code กัน
const initailState = {
value: 0
}

export default (state = initialState, action) => {
switch (action.type) {
case "VALUE_INCREMENT_ONE":
return { ...state, value: state.value+1 }
case "VALUE_MINUS_ONE":
return { ...state, value: state.value-1}
case "CHANGE_VALUE_TO_RECIEVED_VALUE":
return { ...state, value: action.value}
default:
return state;
}
}

Action: ส่วนที่ทำหน้าที่เก็บ action รูปแบบต่างๆไว้แล้วส่งเข้าไปหา component ที่ต้องการเปลี่ยนค่าของ state ตามรูปแบบของ action นั้นๆ มักเขียนเป็น fucntion ที่คืนค่าเป็น object

object ที่คืนออกมาจาก action จะมีลักษณะสำคัญอยู่คือ ต้องมี key ที่ชื่อว่า type เสมอ

เพราะจะเป็นสิ่งที่ระบุตัวตนของ action นี้เมื่อเข้าไปถึง reducer ตามรูปที่ 3 (ลูกษรสีเขียว) และสามารถมี payload อื่นๆเพิ่มเติมได้

export function handleOnChangeAction(value) {
return {
type: "CHANGE_VALUE_TO_RECIEVED_VALUE",
value: value
}
}

Provider
เมื่อเรารู้จักส่วนประกอบของ Redux แล้ว ในส่วนต่อไปคือการนำ redux มา provide ให้ App เข้าถึงได้ ซึ่งส่วนที่รับหน้าที่นี้ก็คือ Provider component นั่นเอง
Provider component มี property ที่สำคัญก็คือ store ซึ่งรับค่า store ที่เราสร้างขึ้นไว้ใน redux จาก createStore() ตามตัวอย่าง code ด้านล่างเลย

import React from 'react'
import ReactDOM from 'react-dom/client'
import { Provider } from 'react-redux'
import App from './app'

ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>,
)

Connect
มาถึงส่วนสุดท้ายแล้ว 🥳 ที่เราจะมาพูดถึงกันเพื่อให้ redux มีวงจรที่สมบูรณ์
เมื่อเรา provide redux ให้ App แล้ว แล้วมี component ต้องการเข้าถึง state เราจะต้องทำสิ่งที่เรียกว่าการ Connect component นั้นเข้ากับ redux เสียก่อน โดยใช้ connect()

import { connect } from "react-redux";
import handleOnChangeAction from "./redux/valueAction"

const mapStateToProps = (state) => {
value: state.valueReducer.value
}

const mapStateToDispatch = {
handleOnChangeAction
}

connect(mapStateToProps, mapDispatchToProps)(<componentYouWantToConnect>)

ในส่วนของการ connect อาจจะเข้าใจยากหน่อย (สำหรับผมอ่ะนะ 55555 🤯) ถ้าใครงงเรามารวมกันตรงนี้ ใครเข้าใจแล้วข้ามได้เลยครับ 😎
อันดับแรก connect เป็น higher order function คือ function ที่มีคุณสมบัติในการคืนค่าออกมาเป็น function อีกตัวได้ เพราะฉะนั้นการ activate fucntion ประเภทนี้จึงจะต้องใช้ 2 วงเล็บต่อท้ายกัน โดยในวงเล็บแรกจะรับ parameter ดังนี้

1. mapStateToProps() สำหรับเชื่อมต่อกับ redux เพื่อส่งค่าของ state มาให้ component โดยสามารถ

2. mapDispatchToProps สำหรับเชื่อมต่อกับ redux เพื่อส่ง function ที่เกี่ยวข้องกับการ setState เข้าไป ซึ่งมักเป็น function action ต่างๆที่เราพูดถึงไปแล้วในส่วนของ action

ในส่วนของวงเล็บที่สองจะรับ parameter เดียวนั่นก็คือ

component เราสามารถส่ง component ที่ต้องการใช้ค่าต่างๆที่มาจาก mapStateToProps และ mapDispatchToProps เข้ามาได้

เฮ้!! เราสามารถรับ-ส่ง state จาก redux ได้แล้ว 🤓

ใน function connect ชื่อของ parameter ไม่ได้สำคัญอะไรเพราะเป็นเพียง conventional เท่านั้น เราสามารถตั้งชื่อเป็นอะไรก็ได้ไม่จำเป็นต้องเป็น mapStateToProps หรือ mapDispatchToProps แต่สิ่งที่สำคัญคือตำแหน่ง เพราะ fucntion connect จะ provide ของที่แตกต่างกันไปให้ parameter แต่ละตำแหน่ง ถ้าอยากให้เกี่ยวกับการรับค่า state เข้ามาให้ใช้ตำแหน่งแรก แต่ถ้าอยากส่ง setState() เข้ามาให้ใช้ตำแหน่งที่สอง

DeepAlert!!

มาถึงจุดนี้ผมขอบอกเลยว่าเพียงมองภาพรวมของ redux ด้านบนออกก็เพียงพอแล้วต่อการนำไปใช้งานจริง แต่ในส่วนนี้จนก่อนถึงหัวข้อถัดไปจะเป็นส่วนลึกที่อยู่ใน connect ที่ผมอยากบันทึกไว้ ซึ่งย้ำว่าไม่จำเป็นต้องรู้ก็ได้ แต่ถ้าอยากรู้ก็มาดูกัน 😂

import React, { useEffect, useState } from 'react';

const connect = (mapStateToProps, mapDispatchToProps) => (WrappedComponent) => {
const ConnectedComponent = (props) => {
const [state, setState] = useState(calculateState());

useEffect(() => {
// Subscribe to Redux store changes
const unsubscribe = store.subscribe(() => {
// Update component state when the store changes
setState(calculateState());
});

// Unsubscribe from Redux store changes when the component is unmounted
return () => {
unsubscribe();
};
}, []);

const calculateState = () => {
// Calculate the props based on mapStateToProps and mapDispatchToProps
const stateProps = mapStateToProps ? mapStateToProps(store.getState(), props) : {};
const dispatchProps = mapDispatchToProps ? mapDispatchToProps(store.dispatch, props) : {};

// Merge state and dispatch props into a single props object
return {
...stateProps,
...dispatchProps,
};
};

// Render the wrapped component with the calculated props
return <WrappedComponent {...props} {...state} />;
};

return ConnectedComponent;
};

ด้านบนนี้คือ code ที่ผมขอให้ chatGPT ช่วย create ให้โดยอ้างอิงจากการทำงานของ function connect เพราะเราไม่สามารถเข้าถึง source ที่แท้จริงของ redux ได้ เพราะฉะนั้นต้องคิดอยู่เสมอว่าของจริงอาจมีอะไรลึกล้ำมากกว่านี้มาก หรือตัว code อาจไม่ใช้แบบนี้เลยโดยสิ้นเชิง อันนี้เป็นเพียงการจำลองขึ้นมาจากสิ่งที่เรารู้จักเพื่อให้เข้าใจกระบวนการทำงานที่เป็นขั้นเป็นตอนของ connect() ได้ง่ายขึ้น 🤓

const connect = (mapStateToProps, mapDispatchToProps) => (WrappedComponent) => {
const ConnectedComponent = (props) => {
...
return <WrappedComponent {...props} {...state} />;
}
return ConnectedComponent;
}

ในส่วนนี้ แสดงให้เห็นว่า connect เป็น higher order fucntion ที่คืนค่ากลับเป็น function

const calculateState = () => {
// Calculate the props based on mapStateToProps and mapDispatchToProps
const stateProps = mapStateToProps ? mapStateToProps(store.getState(), props) : {};
const dispatchProps = mapDispatchToProps ? mapDispatchToProps(store.dispatch, props) : {};

// Merge state and dispatch props into a single props object
return {
...stateProps,
...dispatchProps,
};
};

ในส่วนนี้จะเป็นการจัดการนำเอา mapStateToProps มาดึงเอา state ปัจจุบันจากข้างใน store และ mapDispatchToProps นำเอา action มาติดกับ dispatch เพื่อให้ตัว action สามารถเข้าถึงการเปลี่ยนแปลง state ได้แล้ว return ออกไปเพื่อรอนำไปใส่เป็น props ให้กับ component ต่อไป

useEffect(() => {
// Subscribe to Redux store changes
const unsubscribe = store.subscribe(() => {
// Update component state when the store changes
setState(calculateState());
});

// Unsubscribe from Redux store changes when the component is unmounted
// การเขียน useEffect ในรูปแบบ return หมายถึงเมื่อ component กำลังจะถูกทำลาย
// เมื่อปิด component นั้นแล้ว ให้ทำสิ่งที่อยู่ใน return ด้วยก่อนจะทำลาย component นั้นไป
return () => {
unsubscribe();
};
}, []);

ในส่วนนี้เป็นส่วนที่ไม่ได้มีพูดถึงกันข้างบนเลย แต่ใน store ที่เราสร้างจาก createStore() จะมี method ที่น่าสนใจติดมาด้วยเช่น

  • getState() ซึ่งเราสามารถเรียกใช้ได้เพื่อดูว่าใน store ปัจจุบันมีค่าอะไรเก็บไว้บ้าง
  • dispatch() เราสามารถเอา action ใส่ลงไปเป็น argument ในdispatch ได้เพื่อเชื่อมต่อไปถึง reducer (ตามรูปที่ 3 เส้นสีเขียว)
  • subscribe() รับ callback function เข้าเป็น argument เมื่อ store มีการเปลี่ยนแปลง จะ activate callback function ที่รับเข้ามาให้ทำงาน จากในตัวอย่างนี้คือสั่งให้เซ็ตค่า state ของ component นี้ให้เป็นไปตามสิ่งที่คืนออกมาจาก caculateState()

นี่คือสิ่งที่จะเกิดขึ้นอย่างต่อเนื่องเมื่อเราเรียก connect() เพื่อเชื่อม component สักตัวเข้ากับ redux

มาลองสร้าง app ง่ายๆโดยใช้ redux กัน

เมื่อเราเข้าใจภาพรวมแล้ว ก็พอเรื่องทฤษฎีกันแค่นี้ดีกว่าแล้วเรามาเริ่มลองใช้จริงกันเลยดีกว่า 😆

ทีนี้เราจะมาลองใช้ React framework and Redux มาสร้างหน้า login-logout อย่างง่ายกันครับ เพื่อให้เราเห็นภาพของการเปลี่ยนแปลงของ state และการอัพเดท state รวมถึงการ rerender component เมื่อมีการเปลี่ยนแปลงของ state ด้วย

เริ่มแรกด้วยการ initial react app ขึ้นมาด้วย boilerplate อย่าง vite กัน

yarn create vite

ระหว่างสร้างจะมีถามว่าเราอยากใช้ framework ไหนเลือกเป็น react และ typescript ครับ เพราะ vite ยังรองรับอีกหลาย framework เลยเช่น vue, svelte เป็นต้น

เมื่อสร้างเสร็จแล้วก็ได้เวลาลง node module และ dependencies ที่จำเป็นตาม file package.json

{
"name": "react-redux-101",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview"
},
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-redux": "^9.0.4",
"react-router-dom": "^6.21.0",
"redux": "^5.0.0"
},
"devDependencies": {
"@types/react-redux": "^7.1.33",
"@types/react-router-dom": "^5.3.3",
"@types/react": "^18.2.43",
"@types/react-dom": "^18.2.17",
"@typescript-eslint/eslint-plugin": "^6.14.0",
"@typescript-eslint/parser": "^6.14.0",
"@vitejs/plugin-react": "^4.2.1",
"eslint": "^8.55.0",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.5",
"typescript": "^5.2.2",
"vite": "^5.0.8"
}
}

อ้ะ?! ทำไม dependencies ที่ลงนอกจาก redux แล้วทำไมยังมี react-redux อีกนะ ตัว redux lib นั้นจะเป็นตัวที่คอยบริหาร state ภายใน redux อย่างเดียว และตัว
react-redux lib จะมีหน้าที่ช่วยในการเชื่อมต่อ App และ redux ของเราเข้าด้วยกันได้ง่ายขึ้น

เมื่อจัดการเรื่อง dependencies เสร็จเรียบร้อยแล้ว มาดู structure folder ของ project นี้กันก่อนเลยเพื่อให้ง่ายต่อการทำความเข้าใจครับ

รูปที่ 4 structure folder

เริ่มแรกคือ การสร้าง folder redux ขึ้นมา เพื่อนไว้จัดการเก็บกลุ่มของ redux ไว้ ในที่นี้คือมี login ทำหน้าที่เดี่ยวกับการ handle state ต่างๆในหน้า login เช่น input

รูปที่ 5 login-action.ts
รูปที่ 6 login-reducer.ts

user ทำหน้าที่เก็บข้อมูลของ user ทำหน้าที่แทน DB ชั่วคราว

รูปที่ 7 user-action.ts
รูปที่ 8 user-reducer.ts

และเมื่อมี reducer หลายตัว ก่อนที่จพนำไปใช้ใน createStroe จำเป็นต้องนำมารวมกันเป็นก้อนเดียวก่อนด้วย combineReducer()

รูปที่ 9 reudx/index.ts

เมื่อสร้าง redux ไว้สำหรับจัดการ state แล้ว ต่อไปเราก็สร้างหน้า component ขึ้นมาแล้วจัดการเชื่อม redux เข้าไป

เริ่มที่ login page ก่อน

รูปที่ 10 Login.tsx

สร้างปุ่มสำหรับ logout ใน navbar

รูปที่ 11 NavBar.tsx

เสร็จแล้วอย่าลืมครอบ App ด้วย Provider เพื่อให้ connect() ภายใน App สามารถเข้าถึง store ของ redux ได้

รูปที่ 12 main.tsx

เพียงเท่านี้ก็สามารถสร้างหน้า login-logout อย่างง่ายขึ้นมาได้แล้วโดยหน้าตาจะประมาณนี้

รูปที่ 13 หน้า login บนเว็บ

เมื่อเราลองใส่ user: wichy และ password:123456 แล้วกด login เราก็จะสามารถเข้าหน้า home และกด logout ได้ครับ

อันนี้เป็น repo ที่มี source code อยู่ลองไป clone มาเล่นกันได้นะครับ 😁

ปล. อาจจะมี react-router-dom ปนมาด้วยให้งงเล่นๆนะครับด้วยความอยากทวนของเก่า + มันส์มือไปหน่อย(55555 😅)

ข้อดี ข้อเสีย ของ redux

ข้อดีของ redux

  • จากที่กล่าวมาข้างต้นจะเห็นว่ากราจัดการและรักษาค่าของ state มีความรัดกุมมากเพื่อป้องกันค่าของ state เปลี่ยนแปลงแบบไม่ได้ตั้งใจ
  • ทำให้เราสามารถแยกส่วน logic ของการเขียน fornt end ออกจากการสร้าง component ได้ดีขึ้นทำให้ code ไม่รกอ่านง่ายขึ้น

ข้อเสียของ redux

  • การเก็บ state ไว้ที่เดียวเมื่อ App มีขนาดใหญ่ขึ้นมากๆ จะทำให้ redux มีขนาดใหญ่ขึ้นตาม และไม่มีการจัดการ structure ของ redux ที่ดีเพียงพอจและ code ด้านในจะเริ่มพันกันเองทำให้ยากต่อการไล่หา logic ที่ถูกนำไปใช้ในแต่ละ component
  • อาจมี learning curve ที่สูงเพื่อทำความเข้าใจก่อนนำไปใช้งานจริง

ก็จบไปแล้วครับ สำหรับบทความนี้ขอบคุณมากนะครับทุกคนที่อ่านแบ่งปันเวลาอ่านมาจนถึงตอนจบ หวังว่าทุกคนจะชอบ และเข้าใจใน redux มากขึ้นครับ

สุดท้ายนี้ผมอยากขอบคุณ Reference จากที่ต่างๆ และทุกคนที่มีส่วนร่วมในการอธิบายให้ผมเข้าใจ redux มากขึ้นและทำให้เกิดบทความนี้ขึ้นมาครับ

Ref:

--

--