เมื่อโปรเจคของเราใหญ่มากขึ้นเรื่อยๆ เรามักจะเจอกับปัญหาการจัดการState วันนี้ผมจะมาพาทุกคนไปรู้จักกับReact APIตัวหนึ่งที่ช่วยให้เราสามารถจัดการStateได้สะดวกมากยิ่งขึ้น โดยเฉพาะในโปรเจคขนาดใหญ่
What is Context?
ContextคือReact APIที่ช่วยให้เราสามารถแชร์ข้อมูลระหว่างComponentsได้โดยไม่ต้องส่งProps
เมื่อก่อนสมัยที่เรายังไม่มีContextหากเราต้องการแชร์ข้อมูลกันระหว่างComponentsเราต้องประกาศStateไว้ที่Top-Level Componentและส่งเป็นPropsลงมาท่ีChildren Componentsเพื่อให้แต่ละComponentสามารถใช้ข้อมูลร่วมกันได้
Why?
สมมติว่าจุดแต่ละจุดในแผนภาพด้านบนเป็นตัวแทนของComponentต่างๆ และให้ComponentsสีดำแทนComponentsที่มีการใช้ข้อมูลร่วมกัน ในทางตรงกันข้ามComponentsสีขาวจะไม่มีการใช้ข้อมูลร่วมกันเลย
เนื่องจากLeft ComponentและGrand-child of Right Componentต้องการใช้ข้อมูลร่วมกัน เราจึงต้องประกาศStateที่จะแชร์ข้อมูลไว้ที่Top-Level Componentและส่งเป็นPropsไปให้Left ComponentและGrand-child of Right Componentแต่เนื่องจากComponentนี้ไม่ได้อยู่ติดกับTop-Level Componentมันจึงจำเป็นต้องส่งPropsไปให้Right ComponentและChild of Right Componentก่อน แล้วจึงส่งต่อให้Grand-child of Right Component
คำถามคือทำไมComponentsที่ไม่ได้ใช้ข้อมูลร่วมกับLeft ComponentและGrand-child of Right componentต้องมาทำหน้าที่ส่งต่อPropsให้ด้วย วิธีนี้ทำให้โค้ดของเราดูไม่สบายตาเท่าไหร่นัก และนอกจากนี้หากPropsที่ส่งมามีการเปลี่ยนแปลง Componentsที่ทำหน้าที่ส่งข้อมูลให้ก็จะต้องRe-renderตามไปด้วย โดยที่ไม่จำเป็นเลย
การRe-renderนั้นควรเกิดขึ้นในComponentที่ใช้ข้อมูลนั้นๆเท่านั้น ไม่เช่นนั้นในที่สุดแล้วอาจทำให้เกิดปัญหาด้าน Performance ได้
และนี่จึงเป็นคำตอบของคำถามที่ว่าเรามีContextไว้ทำไม Contextเกิดขึ้นมาเพื่อลบข้อเสียที่ได้กล่าวมาข้างต้น
Contextช่วยให้เราสามารถใช้ข้อมูลร่วมกันระหว่างComponentsได้ โดยที่ไม่ต้องใช้Propsหากเราต้องการใช้ข้อมูลที่ใด ก็เรียกใช้Contextที่Componentนั้นพอ Componentsอื่นๆ และหากข้อมูลภายในContextเปลี่ยนแปลง ก็จะเกิดการRe-renderเฉพาะComponentsที่ใช้Contextนั้นเท่านั้น
นอกจากนี้ Context ยังช่วยทำให้ComponentsของเราสามารถReuseได้ง่ายขึ้นอีกด้วย เนื่องจากมันไม่ได้ผูกกับPropsแล้ว
When?
Context นั้นควรใช้ในกรณีที่จำเป็น เนื่องจากเป็นการเพิ่มความซับซ้อนให้แก่โค้ดของเรา ทำให้เราเจอความลำบากมากขึ้นเมื่อต้องมาแก้ไขโค้ดในภายหลัง
จากDOM Treeด้านบนจะเห็นได้ว่าComponent1,6,7,8ต่างต้องการใช้งานข้อมูลร่วมกัน และแต่ละComponentนั้นอยู่ในระดับที่แตกต่างกันทั้งแนวตั้งและแนวดิ่ง การส่งเป็นPropsลงมาจะทำให้ทุกComponentต้องถือPropsเดียวกัน ทั้งที่บางComponentอาจไม่จำเป็นต้องใช้เลย ดังนั้นจึงควรใช้Contextเพื่อหลีกเลี่ยงปัญหาดังกล่าว
ตัวอย่างกรณีที่ไม่จำเป็นต้องใช้Context เช่นเรามี3Componentsที่ต้องใช้ข้อมูลร่วมกัน แต่ว่าแต่ละComponentอยู่ห่างกันไม่เกิน1Level เคสแบบนี้การประกาศStateที่Component 1และส่งPropsมาที่Component2,4อาจเหมาะสมกว่า เนื่องจากจะเห็นได้ว่าไม่มีComponentไหนที่ได้รับข้อมูลโดยที่ไม่จำเป็นเลย
How?
เมื่อเรารู้จักContextและรู้ว่าควรใช้งานมันเมื่อไหร่แล้ว ก็ถึงเวลาเรียนรู้ว่าจะใช้งานมันได้อย่างไร
ก่อนที่ผมจะแนะนำวิธีการใช้งานContext ต้องขออนุญาตแนะนำContext APIที่จำเป็นต้องรู้จักกันก่อน
React.createContext
APIตัวแรกที่ต้องรู้จักคือcreateContext มันเป็นตัวสร้างContext Objectขึ้นมาเพื่อเก็บข้อมูลที่ต้องการใช้ร่วมกัน, ใช้เป็นProvider, และใช้Subscribe Contextที่Componentsปลายทาง
Context.Provider
อย่างที่เรียนให้ทราบด้านบน การสร้างContext Objectจะมาพร้อมกับProvider ซึ่งเจ้าProviderจะช่วยให้Componentsปลายทางทั้งหลายสามารถSubscribeการเปลี่ยนแปลงข้อมูลภายในContextได้
Class.contextType
ใช้เพื่อSubscribeข้อมูลภายในContextที่Componentsปลายทาง แต่จะใช้ได้กับClass Componentเท่านั้น และหากSubscribe Contextด้วยวิธีนี้เพียงวิธีเดียวจะทำให้เราสามารถSubscribeได้เพียงหนึ่งContextเท่านั้น
Context.Consumer
ใช้เพื่อSubscribeข้อมูลภายในContextที่Componentปลายทาง โดยที่สามารถใช้ได้กับทั้งClassและFunctional Component การSubscribe Contextด้วยวิธีนี้จะทำให้เราสามารถSubscribeได้มากกว่าหนึ่งContext
React.useContext
ใช้เพื่อSubscribeข้อมูลภายในContextที่Componentปลายทาง ใช้ได้กับFunctional Componentเท่านั้น การSubscribe Contextด้วยวิธีนี้จะทำให้เราสามารถSubscribeได้มากกว่าหนึ่งContext
วิธีนี้เป็นวิธีที่ง่ายและนิยมที่สุดในการSubscribeข้อมูลจากContext
Example
เรารู้จักContextกันมามากพอแล้ว สิ่งต่อไปที่เราอยากเห็นคงหนีไม่พ้นตัวอย่างการใช้งาน
ต้องขอบอกว่าวิธีการใช้งานที่ผมกำลังจะนำเสนอเป็นหนึ่งในวิธีการใช้งานเท่านั้นและไม่การันตีว่าจะเป็นวิธีการที่ดีที่สุด
ในที่นี้เราจะสร้างCart Contextเพื่อใช้เก็บข้อมูลสินค้าในรถเข็น
เริ่มแรกเราต้องสร้างContext Objectขึ้นมาก่อน แล้วจึงสร้างCartProvider Componentเพื่อใช้เป็นที่เก็บStateและActionต่างๆของContext
ในCartProviderเราจะสร้างStateขึ้นมาเพื่อใช้เป็นที่เก็บข้อมูลต่างๆที่ต้องการใช้ร่วมกัน และสร้างAction Fucntionต่างๆที่เราจะอนุญาตให้Componentปลายทางสามารถทำได้เช่น addToCart
ต่อมาเราจะสร้างตัวแปรcartStoreขึ้นมาเพื่อเป็นValueให้แก่PropของProvider ซึ่งPropตัวนี้จะเป็นค่าที่เราจะส่งไปให้Consumerนั่นเอง
ซึ่งค่าของตัวแปรcartStoreผมดีไซน์แยกออกเป็นสองส่วนคือส่วนของStateและAction จากโค้ดจะเห็นได้ว่าStateจะอยู่ในLevelของroot ส่วนActionจะถูกwrapไว้ด้วยkey cartAction อีกที
วิธีนี้ทำให้เมื่อเราอ่านโค้ดที่Componentปลายทาง เราสามารถเข้าใจได้ในทันทีเลยว่ามันกำลังใช้ค่าจากStateหรือกำลังจะทำActionอะไรบางอย่างกับContextอยู่
สุดท้ายเราจะสร้างCustom Hookชื่อว่าuseCartContextขึ้นมา ประโยชน์ของHookตัวนี้คือทำให้เราไม่ต้องImport useContextและCartContextในทุกComponentปลายทาง ที่ต้องการSubscribe เพียงImport useCartContextไปตัวเดียวก็สามารถใช้ข้อมูลของContextได้แล้ว ทำให้โค้ดคลีนขึ้น
เมื่อสร้างCartProviderเสร็จแล้ว เราก็จะนำProvider ตัวนี้ไปครอบเป็นParent Component ที่ระดับApp levelเพื่อให้ทุกComponentในแอปของเราสามารถเรียกใช้งานContextตัวนี้ได้นั่นเอง
หากเรามีมากกว่าหนึ่งContextเราก็สามารถสร้างProviderอื่นๆขึ้นมาได้และนำมาใช้ในรูปแบบเดียวกัน
หากComponentไหนต้องการใช้งานCartContextเพียงแค่Import useCartContextมาใช้ ก็จะสามารถใช้งานได้โดยที่ข้อมูลที่ได้รับจะเป็นข้อมูลที่แชร์กันระหว่างComponents
หากเราต้องการSubscribe Context Valueที่Class Componentเราอาจสร้างHigh Order Componentขึ้นมาตัวหนึ่งเพื่อใช้ส่งContext Valueไปเป็นPropให้กับComponentปลายทาง
Caution!!!
จุดที่ทุกคนควรรู้และระวังคือContextไม่มีการพิจารณาว่าComponentsปลายทางสมควรที่จะถูกRe-renderหรือไม่ เพราะฉะนั้นทุกครั้งที่ข้อมูลภายในContextเปลี่ยนแปลง Componentปลายทางจะถูกสั่งRe-renderด้วย
หากเราต้องการให้โค้ดบางส่วนทำงานก็ต่อเมื่อข้อมูลในContextบางตัวเปลี่ยนเท่านั้น ไม่ใช่ทุกตัว เราอาจใช้useMemoเข้ามาช่วยได้ครับ
หากสนใจศึกษาข้อมูลเกี่ยวกับContext APIเพิ่มเติม สามารถดูได้จากOfficial Documentเลยครับ
Source : https://medium.com/@pakpoom.thawee/%E0%B8%A1%E0%B8%B2%E0%B8%97%E0%B8%B3%E0%B8%84%E0%B8%A7%E0%B8%B2%E0%B8%A1%E0%B8%A3%E0%B8%B9%E0%B9%89%E0%B8%88%E0%B8%B1%E0%B8%81-react-context-api-%E0%B8%81%E0%B8%B1%E0%B8%99%E0%B9%80%E0%B8%96%E0%B8%AD%E0%B8%B0-511d1bd332f1
No comments:
Post a Comment