ไทย

เชี่ยวชาญ React Context เพื่อการจัดการ state ที่มีประสิทธิภาพในแอปพลิเคชันของคุณ เรียนรู้ว่าเมื่อใดควรใช้ Context วิธีการใช้งานอย่างมีประสิทธิผล และหลีกเลี่ยงข้อผิดพลาดที่พบบ่อย

React Context: คู่มือฉบับสมบูรณ์

React Context เป็นฟีเจอร์ที่ทรงพลังซึ่งช่วยให้คุณสามารถแชร์ข้อมูลระหว่างคอมโพเนนต์ได้โดยไม่ต้องส่ง props ผ่านทุกระดับของโครงสร้างคอมโพเนนต์ (component tree) อย่างชัดเจน มันเป็นวิธีการทำให้ค่าบางอย่างพร้อมใช้งานสำหรับทุกคอมโพเนนต์ใน subtree ที่เฉพาะเจาะจง คู่มือนี้จะสำรวจว่าเมื่อใดและอย่างไรที่จะใช้ React Context อย่างมีประสิทธิภาพ พร้อมกับแนวทางปฏิบัติที่ดีที่สุดและข้อผิดพลาดทั่วไปที่ควรหลีกเลี่ยง

ทำความเข้าใจปัญหา: Prop Drilling

ในแอปพลิเคชัน React ที่ซับซ้อน คุณอาจพบกับปัญหา "prop drilling" ซึ่งเกิดขึ้นเมื่อคุณต้องการส่งข้อมูลจากคอมโพเนントแม่ (parent component) ไปยังคอมโพเนนต์ลูกที่ซ้อนกันอยู่ลึกๆ ในการทำเช่นนี้ คุณต้องส่งข้อมูลผ่านคอมโพเนนต์กลางทุกตัว แม้ว่าคอมโพเนนต์เหล่านั้นจะไม่ต้องการข้อมูลนั้นเลยก็ตาม ซึ่งอาจนำไปสู่:

พิจารณาตัวอย่างง่ายๆ ต่อไปนี้:


function App() {
  const user = { name: 'Alice', theme: 'dark' };
  return (
    <Layout user={user} />
  );
}

function Layout({ user }) {
  return (
    <Header user={user} />
  );
}

function Header({ user }) {
  return (
    <Navigation user={user} />
  );
}

function Navigation({ user }) {
  return (
    <Profile user={user} />
  );
}

function Profile({ user }) {
  return (
    <p>Welcome, {user.name}!
Theme: {user.theme}</p>
  );
}

ในตัวอย่างนี้ อ็อบเจกต์ user ถูกส่งผ่านคอมโพเนนต์หลายตัว แม้ว่าจะมีเพียงคอมโพเนนต์ Profile เท่านั้นที่ใช้งานจริง นี่คือกรณีคลาสสิกของ prop drilling

ขอแนะนำ React Context

React Context เป็นวิธีหลีกเลี่ยง prop drilling โดยการทำให้ข้อมูลพร้อมใช้งานสำหรับคอมโพเนนต์ใดๆ ใน subtree โดยไม่ต้องส่งผ่าน props อย่างชัดเจน ประกอบด้วยส่วนหลักสามส่วน:

เมื่อใดที่ควรใช้ React Context

React Context มีประโยชน์อย่างยิ่งสำหรับการแชร์ข้อมูลที่ถือว่าเป็น "ส่วนกลาง" (global) สำหรับโครงสร้างคอมโพเนนต์ React ซึ่งอาจรวมถึง:

ข้อควรพิจารณาที่สำคัญ:

วิธีใช้ React Context: ตัวอย่างการใช้งานจริง

เรามาดูตัวอย่าง prop drilling อีกครั้งและแก้ไขปัญหานี้โดยใช้ React Context

1. สร้าง Context

ขั้นแรก สร้าง context โดยใช้ React.createContext() context นี้จะเก็บข้อมูลผู้ใช้


// UserContext.js
import React from 'react';

const UserContext = React.createContext(null); // Default value can be null or an initial user object

export default UserContext;

2. สร้าง Provider

ถัดไป ครอบคอมโพเนนต์รากของแอปพลิเคชันของคุณ (หรือ subtree ที่เกี่ยวข้อง) ด้วย UserContext.Provider ส่งอ็อบเจกต์ user เป็น prop ที่ชื่อ value ไปยัง Provider


// App.js
import React from 'react';
import UserContext from './UserContext';
import Layout from './Layout';

function App() {
  const user = { name: 'Alice', theme: 'dark' };
  return (
    <UserContext.Provider value={user}>
      <Layout />
    </UserContext.Provider>
  );
}

export default App;

3. ใช้งาน Context

ตอนนี้ คอมโพเนนต์ Profile สามารถเข้าถึงข้อมูล user ได้โดยตรงจาก context โดยใช้ useContext hook ไม่ต้องทำ prop drilling อีกต่อไป!


// Profile.js
import React, { useContext } from 'react';
import UserContext from './UserContext';

function Profile() {
  const user = useContext(UserContext);

  return (
    <p>Welcome, {user.name}!
Theme: {user.theme}</p>
  );
}

export default Profile;

คอมโพเนนต์กลาง (Layout, Header และ Navigation) ไม่จำเป็นต้องรับ prop user อีกต่อไป


// Layout.js, Header.js, Navigation.js
import React from 'react';

function Layout({ children }) {
  return (
    <div>
      <Header />
      <main>{children}</main>
    </div>
  );
}

function Header() {
  return (<Navigation />);
}

function Navigation() {
  return (<Profile />);
}

export default Layout;

การใช้งานขั้นสูงและแนวทางปฏิบัติที่ดีที่สุด

1. การรวม Context กับ useReducer

สำหรับการจัดการ state ที่ซับซ้อนยิ่งขึ้น คุณสามารถรวม React Context กับ useReducer hook ได้ ซึ่งจะช่วยให้คุณจัดการการอัปเดต state ได้อย่างคาดเดาได้และบำรุงรักษาง่ายขึ้น context จะเป็นผู้ให้ state และ reducer จะจัดการกับการเปลี่ยนแปลงของ state ตาม action ที่ถูก dispatch


// ThemeContext.js
import React, { createContext, useReducer } from 'react';

const ThemeContext = createContext();

const initialState = { theme: 'light' };

const themeReducer = (state, action) => {
  switch (action.type) {
    case 'TOGGLE_THEME':
      return { ...state, theme: state.theme === 'light' ? 'dark' : 'light' };
    default:
      return state;
  }
};

function ThemeProvider({ children }) {
  const [state, dispatch] = useReducer(themeReducer, initialState);

  return (
    <ThemeContext.Provider value={{ ...state, dispatch }}>
      {children}
    </ThemeContext.Provider>
  );
}

export { ThemeContext, ThemeProvider };



// ThemeToggle.js
import React, { useContext } from 'react';
import { ThemeContext } from './ThemeContext';

function ThemeToggle() {
  const { theme, dispatch } = useContext(ThemeContext);

  return (
    <button onClick={() => dispatch({ type: 'TOGGLE_THEME' })}>
      Toggle Theme (Current: {theme})
    </button>
  );
}

export default ThemeToggle;



// App.js
import React from 'react';
import { ThemeProvider } from './ThemeContext';
import ThemeToggle from './ThemeToggle';

function App() {
  return (
    <ThemeProvider>
      <div>
        <ThemeToggle />
      </div>
    </ThemeProvider>
  );
}

export default App;

2. การใช้หลาย Contexts

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

3. การปรับปรุงประสิทธิภาพ

ดังที่ได้กล่าวไว้ก่อนหน้านี้ การเปลี่ยนแปลง context อาจทำให้เกิดการ re-render ในคอมโพเนนต์ที่ใช้งาน เพื่อปรับปรุงประสิทธิภาพ ให้พิจารณาสิ่งต่อไปนี้:

4. การใช้ Custom Hooks สำหรับการเข้าถึง Context

สร้าง custom hooks เพื่อห่อหุ้มตรรกะสำหรับการเข้าถึงและอัปเดตค่า context ซึ่งจะช่วยปรับปรุงความสามารถในการอ่านและการบำรุงรักษาโค้ด ตัวอย่างเช่น:


// useTheme.js
import { useContext } from 'react';
import { ThemeContext } from './ThemeContext';

function useTheme() {
  const context = useContext(ThemeContext);
  if (!context) {
    throw new Error('useTheme must be used within a ThemeProvider');
  }
  return context;
}

export default useTheme;



// MyComponent.js
import React from 'react';
import useTheme from './useTheme';

function MyComponent() {
  const { theme, dispatch } = useTheme();

  return (
    <div>
      Current Theme: {theme}
      <button onClick={() => dispatch({ type: 'TOGGLE_THEME' })}>
        Toggle Theme
      </button>
    </div>
  );
}

export default MyComponent;

ข้อผิดพลาดทั่วไปที่ควรหลีกเลี่ยง

ทางเลือกอื่นนอกเหนือจาก React Context

แม้ว่า React Context จะเป็นเครื่องมือที่มีค่า แต่ก็ไม่ใช่ทางออกที่ดีที่สุดเสมอไป ลองพิจารณาทางเลือกเหล่านี้:

สรุป

React Context เป็นฟีเจอร์ที่ทรงพลังสำหรับการแชร์ข้อมูลระหว่างคอมโพเนนต์โดยไม่ต้องทำ prop drilling การทำความเข้าใจว่าเมื่อใดและอย่างไรที่จะใช้อย่างมีประสิทธิภาพเป็นสิ่งสำคัญสำหรับการสร้างแอปพลิเคชัน React ที่บำรุงรักษาง่ายและมีประสิทธิภาพสูง โดยการปฏิบัติตามแนวทางปฏิบัติที่ดีที่สุดที่ระบุไว้ในคู่มือนี้และหลีกเลี่ยงข้อผิดพลาดทั่วไป คุณสามารถใช้ประโยชน์จาก React Context เพื่อปรับปรุงโค้ดของคุณและสร้างประสบการณ์ผู้ใช้ที่ดีขึ้น อย่าลืมประเมินความต้องการเฉพาะของคุณและพิจารณาทางเลือกอื่นก่อนที่จะตัดสินใจว่าจะใช้ Context หรือไม่