[React] มาทำ global state อย่างง่ายๆ ด้วย react hooks กันเถอะ

Chanapai Suparbpong
THE EXISTING COMPANY
3 min readJan 13, 2020

สำหรับคนที่เริ่มต้นเขียน react ใหม่ๆแล้ว อยากจะทำ global state หลายๆสำนักคงแนะนำให้ใช้ redux, mobx บลาๆ ซึ่งมันเป็นอะไรที่ยากและวุ่นวายสำหรับผู้เริ่มต้นเขียน react เป็นอย่างมาก

วันนี้ทางเจ้าของบล็อกจะมาแนะนำการทำ global state อย่างง่าย ด้วย react hooks ซึ่งจะมี function หลักๆของ hooks คือ useContext และ useReducer

ถ้าเพื่อนๆพี่ๆคนไหนไม่รู้จัก react hooks ลองไปศึกษาก่อนได้ที่

UseContext คือ?

  • Hook API ที่ช่วยในการจัดการกับ global state คล้าย ๆ กับ redux ซึ่งหลักการของเจ้าตัว Context Api มันจะเป็นการไปบอก component ที่ชั้น parent หรือ top level component ว่าจะให้ค่า context เป็นอย่างไร แล้ว component ลูกที่อยู่ใต้มันจะสามารถนำค่า context ไปใช้ได้

UseReducer คือ?

  • จะเป็นตัวช่วยในการจัดการ state ของเรา ซึ่งถ้าใครที่เคยเขียน redux มาก็จะคุ้นเคยกับเจ้าตัว reducer เป็นอย่างดี

เอาละมาเริ่มกันเถอะ !!

ก่อนอื่นก็สร้าง react app กันก่อน

npx create-react-app my-app

ใครจะสร้างแบบ js , ts หรือไม่ใช้ create-react-app ก็แล้วแต่ความลำบากเลยนะครับ
แต่วันนี้ทางเจ้าของบล็อกจะสร้างแบบใช้ js ธรรมดาเพื่อง่ายต่อทางเจ้าของบล็อกและผู้อ่าน

ก่อนที่เริ่มเขียน code กันต่อนั้น วันนี้ทางเจ้าของบล็อกจะลองทำ feature Counter แบบง่ายๆ โดยการกดปุ่มเพิ่ม ลบ อาจจะไม่ได้วาง structure หรือแบ่ง component อะไรมากมายเพราะคนเขียนแอบขี้เกียจ ฮ่าๆๆ

Folder structure ของ my-app

มาเริ่มกันที่ store/CounterProvider.js

สร้าง Counter Context สำหรับเก็บ state ของเราก่อน

File store/CounterProvider.js

หลังจากนั้นก็ initalState ตามที่เราต้องการได้เลยครับ
ต่อมาในส่วนของ counterReducer ถ้าคนเคยเขียน redux มาก็จะร้องอ๋อทันที
เหมือนกันเป๊ะๆ แต่สำหรับผู้เริ่มต้นใหม่ก็ไม่ยากอย่างที่คิดครับ
พารามิเตอร์ที่อยู่ใน function counterReducer จะมีหลักๆ 2 ตัวคือ state, action
ซึ่ง state ก็คือ ตัวแปรที่อยู่ใน initalState นั่นแหละ ส่วน action คือ object ที่ถูกส่งมาจากการ dispatch ผ่าน useReducer นั่นเองครับ

ในส่วนของ function CounterProvider ก็จะนำ useReducer มาใช้ครับ

พอทำทุกอย่างเสร็จแล้ว เราจะส่งค่าหรือ function อะไรไปให้ child component ใช้ เราก็ยัดลง Provider ได้เลย ตามรูปข้างบนครับ

วิธีนำมาใช้งาน

โดยการใช้งานจะเหมือนการใช้ Provider ทั่วๆไป เลยครับ เอามาไว้ที่ top level component ซึ่งครั้งนี้ทางเจ้าของบล็อกจะนำไปไว้ที่ index.js

ต่อมาเรามาสร้าง ui ฉบับคนขี้เกียจกันเถอะ ฮ่าๆ
ไปที่ App.js ของเราเลยครับ ลบอะไรที่ไม่ได้ใช้ออกไปให้หมด จะได้ไม่รกตา

ในส่วนของการจะเอาค่าหรือ function ต่างๆที่เราสร้างไว้ใน CounterProvider มาใช้นั้น ให้เรา import useContext กับ CounterContext ที่สร้างไว้ตอนแรกได้เลยครับ
วิธีใช้ก็ตามรูปด้านล่างเลย อยากดึงอะไรมาใช้ก็ destructure ค่าออกมาได้เลย easy มากๆๆๆๆ

File App.js

ตอนนี้ทุกอย่างเสร็จแล้วก็ start app ของเราได้เลยครับ

npm run start 

ก่อนจะจากกันไปวันนี้มี Bonus ทิ้งท้ายเพื่อให้ชีวิตง่ายขึ้นในการใช้ useContext และ useReducer นะครับ

Bonus 1

  • เวลาที่เรานำ Context ไปคลุมไว้ที่ top level component นั้น ถ้าเกิดเรามีหลายๆ Context จะเกิดอะไรขึ้น
File Index.js

มันก็จะซ้อนลงกันไปเรื่อยๆแบบนี้ ซึ่งถ้าเรามี Context เป็น 10 20 30 40 ก็คงจะไม่มีใครโอเคใช่ไหมละ ฮ่าๆๆๆ จึงได้มีคนคิดค้นวิธีการแก้ปัญหานี้มา

ไปที่ store/providerComposer.js

วิธีใช้งานก็กลับมาที่ index.js ครับ

File index.js

ลองศึกษาตาม link ข้างล่างได้เลยครับ
https://tobea.dev/post/global-state-cleanliness-with-react-context

Bonus 2

  • ใน function counterReducer เวลาเราจะ setState ใหม่แต่ละครั้ง เราจะต้อง return แล้วก็ …state เพื่อเอาค่า state เก็บไว้ก่อน แล้วจะ setState อะไรใหม่อีกก็เรื่องของเรา ซึ่งถ้ามันมีหลายๆ state ก็อาจจะทำให้อ่านยาก
    เรามี lib มาแนะนำคือ use-immer ซึ่งจะทำให้การ setState ใน reducer เป็นเรื่องที่ง่าย อ่านแว็บเดียวก็สามารถเข้าใจได้เลย

สรุป

ถึงแม้การใช้ react hooks ในการทำ global state นั้น จะไม่มี plug in หรือ tools ต่างๆเหมือน redux แต่สำหรับผู้เริ่มต้นใหม่ที่อยากจะลองใช้ global state แต่ขี้เกียจไปเสียเวลานั่งศึกษา redux เจ้าตัว useContext , useReducer ก็อาจจะตอบโจทย์ของเพื่อนๆพี่ๆหลายคนนะครับ ไว้โอกาสหน้ามาพบกันใหม่ สวัสดีครับ

Demo Code :

--

--