ตั้งใจว่าจะจด note สำหรับการสร้าง Context ใน React ไว้ ดังนี้บทความนี้จะเป็นตัวอย่างการใช้งาน context เท่านั้น อาจจะไม่ได้ลงรายละเอียดที่มาที่ไปไว้เยอะเท่าไหร่ แต่ก็มีอยู่บ้าง


เมื่อไหร่ที่ต้องใช้งาน Context

แนวคิดหลักของการใช้ Context คือ การอนุญาตให้ component ของเรา สามารถเข้าถึงข้อมูลที่เป็นข้อมูลส่วนกลาง และ re-render อีกครั้ง เมื่อข้อมูลส่วนกลางเหล่านั้นมีการเปลี่ยนแปลง

Context ถูกสร้างมาเพื่อช่วยแก้ปัญหาการส่งต่อข้อมูล เมื่อเราต้องส่งต่อข้อมูลผ่าน props จาก parents ไปยัง children (Prop drilling) ได้

จากเดิมที่ต้องส่งผ่าน props ต่อๆ กันไปเรื่อยๆ

Prop drilling

ก็เปลี่ยนเป็นเรียกผ่าน context แทน ไม่ต้องส่งข้าม props ต่อกันต่อไปแล้ว

Using context

สามารถไปอ่านเพิ่มเติมได้ที่ Passing Data Deeply with Context

เพราะฉนั้นเราสามารถบอกได้ว่า ถ้าหากเมื่อไหร่ที่มีการส่ง props ไปยัง component ลูก ที่ห่างกันมากๆ แล้ว ก็ให้ใช้ context ในการส่งค่า และหากว่าต้องการแชร์ค่าเหล่านั้นให้ component อื่นใช้ด้วย ก็ให้ใช้ context เช่นกัน

เช่น

  • global state
  • theme
  • application configuration
  • authenticated user name
  • user settings
  • preferred language
  • a collection of services

วิธีการใช้งาน context

การใช้ context ใน React ต้องมี 3 ขั้นตอนง่ายๆ เลย คือ

  1. การสร้าง (Creating the context)
  2. การระบุ (Providing the context)
  3. การใช้งาน (Consuming the context)

1.การสร้าง context

เริ่มต้นที่การสร้าง context กันก่อน โดยเราสามารถสร้าง context ได้โดยใช้ฟังก์ชั่น createContext(default value)

ตัวอย่าง:

import { createContext } from 'react';

export const Context = createContext({
  count: 0
});

context.ts

2.การระบุ context

ต่อมาเราต้องมากำหนดตัว Provider ให้แก่ component ที่ต้องการใช้งาน context นั้นๆ ซึ่งโดยทั่วไปแล้วเราจะกำหนดไว้ที่ component บนสุด (Main) ของ component ลูกๆ ที่จะใช้งาน context นั้น

import { Context } from './context'
import Counter from './counter'

const Main = () => {
  const value = { count: 0 }

  return (
    <Context.Provider value={value}>
      <Counter />
    </Context.Provider>
  )
}

export default Main

main.tsx

จาก code จะเห็นว่า เราจะทำการ import { Context } from './context' ที่ถูกสร้างจากด้านบน มาใช้ และระบุ .Provider ต่อท้าย context นั้น และอีกส่วนหนึ่ง คือ เราจะต้องส่งค่า value ซึ่งเป็นค่าเริ่มต้นไปด้วย

3.การใช้งาน context

หลังจากที่เราได้ระบุ context ที่ต้องการจะใช้ไว้ที่ component บนสุดแล้ว ตอนนี้เราก็สามารถเรียกใช้งาน context นั้นจาก component ไหนก็ได้ ที่อยู่ภายใต้ component ที่ถูกประกาศ Context.Provider นี้

โดยสามารถเรียกใช้งานได้ด้วยการ import { Context } from './context' และเรียกใช้งานผ่านฟังก์ชัน hook ที่ชื่อว่า useContext(Context) ซึ่งมันจะ return ค่า จาก context กลับมาให้

import { useContext } from 'react';
import { Context } from './context';

const Counter = () => {
  const { count } = useContext(Context);

  return <span>{count}</span>;
}

export default Counter

counter.ts

ดังนี้เราสามารถสรุปการทำงานของมันได้ดังภาพนี้


การแก้ไขข้อมูลจาก component ลูก (Updating Context)

เสริมต่อจากข้อที่ 3 แล้วถ้าเราอยากจะ update ข้อมูลใน component ต้องทำยังไง?

ตัวอย่าง

ปรับ context ของเรา เพิ่มฟังก์ชั่น increment, decrement เข้าไป ซึ่งเป็นฟังก์ชั่นว่างๆ

import { createContext } from 'react';

export const Context = createContext({
  count: 0,
  increment: () => {},
  decrement: () => {}
});

context.tsx

จากนั้นใน main.tsx เราจะ implement ฟังก์ชั่น เพิ่ม increment, decrement เข้าไป โดยให้ฟังก์ชั่นนั้นจัดการ set state ของจำนวน count ไว้

import { useState } from 'react'
import { Context } from './context'
import Counter from './counter'

const Main = () => {
  const [count, setCount] = useState(0)

  const value = {
    count: count,
    increment: () => {
      setCount((count) => count + 1)
    },
    decrement: () => {
      setCount((count) => count - 1)
    }
  }

  return (
    <Context.Provider value={value}>
      <Counter />
    </Context.Provider>
  )
}

export default Main

main.tsx

อธิบายแบบง่ายๆ เลย คือ เราจะส่งฟังก์ชั่น increment, decrement ที่มีฟังก์ชั่น setCount อยู่ในนั้น เพื่อให้ component ลูกได้เรียกใช้ เพื่ออัพเดทข้อมูล

ใน component counter ก็เรียกใช้ฟังก์ชั่น increment, decrement ผ่านทาง useContext ได้เลย

import { useContext } from 'react'
import { Context } from './context'

const Counter = () => {
  const { count, increment, decrement } = useContext(Context)

  return (
    <div>
      <h1>Count: {count}</h1>
      <button id='incrementButton' onClick={decrement}>
        -
      </button>
      <button id='decrementButton' onClick={increment}>
        +
      </button>
    </div>
  )
}

export default Counter

counter.tsx

เท่านี้ เราก็สามารถสร้าง context ที่สามารถอัพเดทข้อมูลจาก component ลูก ได้แล้ว

ลองนำเอาไปประยุกค์ใช้กันดูนะ


สุดท้าย

ก่อนที่จะหยิบเอา context มาใช้งาน เราควรคิดให้รอบคอบก่อนตัดสินใจว่าจะใช้ context ไหม เพราะถ้าหากเราเอาทุกอย่างไปฝากไว้ที่ context มันอาจจะทำให้ context มันมีความซับซ้อนมากขึ้นตามไปด้วย ซึ่งมันจะยากต่อการใช้งานและการดูแลรักษามันได้ อีกอย่างนึงคือ ยิ่งซับซ้อนยิ่งเขียน unit testing ยาก

ดังนั้นการใช้งาน context เราต้องคิดด้วยว่าจะเอาโค๊ดของเราไว้ตรงไหนบ้าง เพื่อให้ง่ายต่อการทดสอบและดูแลรักษามัน


Reference: