ไทย

ปลดล็อกพลังของรูปแบบ Render Props ใน React เรียนรู้วิธีการส่งเสริมการใช้โค้ดซ้ำ การประกอบคอมโพเนนต์ และการแยกส่วนความรับผิดชอบ เพื่อสร้างแอปพลิเคชันที่ยืดหยุ่นและบำรุงรักษาง่ายสำหรับผู้ชมในระดับสากล

รูปแบบ Render Props ของ React: ตรรกะส่วนประกอบที่ยืดหยุ่นสำหรับผู้ชมทั่วโลก

ในแวดวงการพัฒนา front-end ที่เปลี่ยนแปลงอยู่ตลอดเวลา โดยเฉพาะอย่างยิ่งในระบบนิเวศของ React รูปแบบสถาปัตยกรรมมีบทบาทสำคัญในการสร้างคอมโพเนนต์ที่สามารถขยายขนาด บำรุงรักษาได้ และนำกลับมาใช้ใหม่ได้ ในบรรดารูปแบบเหล่านี้ รูปแบบ Render Props โดดเด่นในฐานะเทคนิคที่ทรงพลังสำหรับการแบ่งปันโค้ดและตรรกะระหว่างคอมโพเนนต์ของ React บล็อกโพสต์นี้มีจุดมุ่งหมายเพื่อให้ความเข้าใจที่ครอบคลุมเกี่ยวกับรูปแบบ Render Props ประโยชน์ กรณีการใช้งาน และวิธีการที่มันช่วยในการสร้างแอปพลิเคชันที่แข็งแกร่งและปรับเปลี่ยนได้สำหรับผู้ชมทั่วโลก

Render Props คืออะไร?

Render Prop เป็นเทคนิคที่เรียบง่ายสำหรับการแบ่งปันโค้ดระหว่างคอมโพเนนต์ของ React โดยใช้ prop ที่มีค่าเป็นฟังก์ชัน โดยพื้นฐานแล้ว คอมโพเนนต์ที่มี render prop จะรับฟังก์ชันที่คืนค่า React element และเรียกใช้ฟังก์ชันนี้เพื่อแสดงผลบางอย่าง คอมโพเนนต์ไม่ได้ตัดสินใจว่าจะแสดงผลอะไรโดยตรง แต่จะมอบหมายการตัดสินใจนั้นให้กับฟังก์ชัน render prop โดยให้สิทธิ์ในการเข้าถึงสถานะภายในและตรรกะของมัน

พิจารณาตัวอย่างพื้นฐานนี้:


class DataProvider extends React.Component {
  constructor(props) {
    super(props);
    this.state = { data: null };
  }

  componentDidMount() {
    // Simulate fetching data
    setTimeout(() => {
      this.setState({ data: 'Some data from an API' });
    }, 1000);
  }

  render() {
    return this.props.render(this.state.data);
  }
}

function MyComponent() {
  return (
     (
        
{data ?

Data: {data}

:

Loading...

}
)} /> ); }

ในตัวอย่างนี้ DataProvider จะดึงข้อมูลและส่งต่อไปยังฟังก์ชัน render prop ที่จัดหาโดย MyComponent จากนั้น MyComponent จะใช้ข้อมูลนี้เพื่อแสดงผลเนื้อหาของตน

ทำไมต้องใช้ Render Props?

รูปแบบ Render Props มีข้อดีที่สำคัญหลายประการ:

กรณีการใช้งานจริงและตัวอย่างในระดับสากล

รูปแบบ Render Props มีคุณค่าในสถานการณ์ที่หลากหลาย นี่คือกรณีการใช้งานทั่วไปพร้อมตัวอย่างที่คำนึงถึงผู้ชมทั่วโลก:

1. การติดตามเมาส์ (Mouse Tracking)

ลองนึกภาพว่าคุณต้องการติดตามตำแหน่งของเมาส์บนหน้าเว็บ ด้วยการใช้ Render Prop คุณสามารถสร้างคอมโพเนนต์ MouseTracker ที่ให้พิกัดของเมาส์แก่คอมโพเนนต์ลูกของมัน


class MouseTracker extends React.Component {
  constructor(props) {
    super(props);
    this.state = { x: 0, y: 0 };
  }

  handleMouseMove = event => {
    this.setState({ x: event.clientX, y: event.clientY });
  };

  render() {
    return (
      
{this.props.render(this.state)}
); } } function MyComponent() { return ( (

The mouse position is ({x}, {y})

)} /> ); }

สิ่งนี้สามารถปรับใช้กับแอปพลิเคชันที่รองรับหลายภาษาได้อย่างง่ายดาย ตัวอย่างเช่น ลองนึกภาพแอปพลิเคชันวาดภาพที่ใช้โดยศิลปินในญี่ปุ่น พิกัดของเมาส์สามารถใช้เพื่อควบคุมฝีแปรงได้:


 (
    
  )}
/>

2. การดึงข้อมูลจาก API

การดึงข้อมูลจาก API เป็นงานทั่วไปในการพัฒนาเว็บ คอมโพเนนต์ Render Prop สามารถจัดการตรรกะการดึงข้อมูลและให้ข้อมูลแก่คอมโพเนนต์ลูกของมัน


class APIFetcher extends React.Component {
  constructor(props) {
    super(props);
    this.state = { data: null, loading: true, error: null };
  }

  async componentDidMount() {
    try {
      const response = await fetch(this.props.url);
      const data = await response.json();
      this.setState({ data: data, loading: false });
    } catch (error) {
      this.setState({ error: error, loading: false });
    }
  }

  render() {
    return this.props.render(this.state);
  }
}

function MyComponent() {
  return (
     {
        if (loading) return 

Loading...

; if (error) return

Error: {error.message}

; return
{JSON.stringify(data, null, 2)}
; }} /> ); }

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


 {
    if (loading) return 

Loading exchange rates...

; if (error) return

Error fetching exchange rates.

; return (
    {Object.entries(data.rates).map(([currency, rate]) => (
  • {currency}: {rate}
  • ))}
); }} />

3. การจัดการฟอร์ม (Form Handling)

การจัดการสถานะของฟอร์มและการตรวจสอบความถูกต้องอาจมีความซับซ้อน คอมโพเนนต์ Render Prop สามารถห่อหุ้มตรรกะของฟอร์มและให้สถานะของฟอร์มและตัวจัดการแก่คอมโพเนนต์ลูกของมัน


class FormHandler extends React.Component {
  constructor(props) {
    super(props);
    this.state = { value: '', error: null };
  }

  handleChange = event => {
    this.setState({ value: event.target.value });
  };

  handleSubmit = event => {
    event.preventDefault();
    if (this.state.value.length < 5) {
      this.setState({ error: 'Value must be at least 5 characters long.' });
      return;
    }
    this.setState({ error: null });
    this.props.onSubmit(this.state.value);
  };

  render() {
    return this.props.render({
      value: this.state.value,
      handleChange: this.handleChange,
      handleSubmit: this.handleSubmit,
      error: this.state.error
    });
  }
}

function MyComponent() {
  return (
     alert(`Submitted value: ${value}`)}
      render={({ value, handleChange, handleSubmit, error }) => (
        
{error &&

{error}

}
)} /> ); }

พิจารณาการปรับเปลี่ยนกฎการตรวจสอบความถูกต้องของฟอร์มเพื่อรองรับรูปแบบที่อยู่ในระดับสากล คอมโพเนนต์ `FormHandler` สามารถคงความเป็นทั่วไปได้ ในขณะที่ render prop จะกำหนดการตรวจสอบความถูกต้องและตรรกะ UI เฉพาะสำหรับภูมิภาคต่างๆ:


 sendAddressToServer(address)}
  render={({ value, handleChange, handleSubmit, error }) => (
    
{/* Fields for address, adapting to regional formats */} {error &&

{error}

}
)} />

4. Feature Flags และการทดสอบ A/B

Render Props ยังสามารถใช้เพื่อจัดการ feature flags และทำการทดสอบ A/B ได้อีกด้วย คอมโพเนนต์ Render Prop สามารถกำหนดได้ว่าจะแสดงผลฟีเจอร์เวอร์ชันใดโดยพิจารณาจากผู้ใช้ปัจจุบันหรือ flag ที่สร้างขึ้นแบบสุ่ม


class FeatureFlag extends React.Component {
  constructor(props) {
    super(props);
    this.state = { enabled: Math.random() < this.props.probability };
  }

  render() {
    return this.props.render(this.state.enabled);
  }
}

function MyComponent() {
  return (
     {
        if (enabled) {
          return 

New Feature!

; } else { return

Old Feature

; } }} /> ); }

เมื่อทำการทดสอบ A/B สำหรับผู้ชมทั่วโลก สิ่งสำคัญคือต้องแบ่งกลุ่มผู้ใช้ตามภาษา ภูมิภาค หรือข้อมูลประชากรอื่นๆ คอมโพเนนต์ `FeatureFlag` สามารถปรับเปลี่ยนเพื่อพิจารณาปัจจัยเหล่านี้เมื่อกำหนดว่าจะแสดงฟีเจอร์เวอร์ชันใด:


 {
    return isEnabled ?  : ;
  }}
/>

ทางเลือกอื่นนอกเหนือจาก Render Props: Higher-Order Components (HOCs) และ Hooks

แม้ว่า Render Props จะเป็นรูปแบบที่ทรงพลัง แต่ก็มีแนวทางทางเลือกที่สามารถให้ผลลัพธ์ที่คล้ายคลึงกันได้ สองทางเลือกที่นิยมคือ Higher-Order Components (HOCs) และ Hooks

Higher-Order Components (HOCs)

Higher-Order Component (HOC) เป็นฟังก์ชันที่รับคอมโพเนนต์เป็นอาร์กิวเมนต์และคืนค่าคอมโพเนนต์ใหม่ที่ได้รับการปรับปรุง HOCs มักใช้เพื่อเพิ่มฟังก์ชันการทำงานหรือตรรกะให้กับคอมโพเนนต์ที่มีอยู่

ตัวอย่างเช่น HOC withMouse สามารถให้ฟังก์ชันการติดตามเมาส์แก่คอมโพเนนต์ได้:


function withMouse(WrappedComponent) {
  return class extends React.Component {
    constructor(props) {
      super(props);
      this.state = { x: 0, y: 0 };
    }

    handleMouseMove = event => {
      this.setState({ x: event.clientX, y: event.clientY });
    };

    render() {
      return (
        
); } }; } function MyComponent(props) { return (

The mouse position is ({props.mouse.x}, {props.mouse.y})

); } const EnhancedComponent = withMouse(MyComponent);

แม้ว่า HOCs จะนำเสนอการใช้โค้ดซ้ำ แต่ก็อาจนำไปสู่การชนกันของชื่อ prop และทำให้การประกอบคอมโพเนนต์ยากขึ้น ซึ่งเป็นปรากฏการณ์ที่เรียกว่า "wrapper hell"

Hooks

React Hooks ซึ่งเปิดตัวใน React 16.8 เป็นวิธีที่ตรงไปตรงมาและสื่อความหมายได้ดีกว่าในการนำตรรกะที่มีสถานะ (stateful logic) กลับมาใช้ใหม่ระหว่างคอมโพเนนต์ Hooks ช่วยให้คุณสามารถ "hook" เข้าไปในสถานะและคุณสมบัติต่างๆ ของ lifecycle ของ React จาก function components ได้

ด้วยการใช้ hook useMousePosition ฟังก์ชันการติดตามเมาส์สามารถนำไปใช้ได้ดังนี้:


import { useState, useEffect } from 'react';

function useMousePosition() {
  const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 });

  useEffect(() => {
    function handleMouseMove(event) {
      setMousePosition({ x: event.clientX, y: event.clientY });
    }

    window.addEventListener('mousemove', handleMouseMove);

    return () => {
      window.removeEventListener('mousemove', handleMouseMove);
    };
  }, []);

  return mousePosition;
}

function MyComponent() {
  const mousePosition = useMousePosition();
  return (
    

The mouse position is ({mousePosition.x}, {mousePosition.y})

); }

Hooks นำเสนอวิธีที่สะอาดและกระชับกว่าในการนำตรรกะที่มีสถานะกลับมาใช้ใหม่เมื่อเทียบกับ Render Props และ HOCs นอกจากนี้ยังส่งเสริมความสามารถในการอ่านและบำรุงรักษาโค้ดได้ดีขึ้น

Render Props กับ Hooks: การเลือกเครื่องมือที่เหมาะสม

การตัดสินใจระหว่าง Render Props และ Hooks ขึ้นอยู่กับความต้องการเฉพาะของโครงการและความชอบส่วนตัวของคุณ นี่คือบทสรุปของความแตกต่างที่สำคัญ:

แนวปฏิบัติที่ดีที่สุดสำหรับการใช้ Render Props

เพื่อใช้รูปแบบ Render Props อย่างมีประสิทธิภาพ ให้พิจารณาแนวปฏิบัติที่ดีที่สุดต่อไปนี้:

สรุป

รูปแบบ Render Props เป็นเทคนิคที่มีคุณค่าสำหรับการสร้างคอมโพเนนต์ React ที่ยืดหยุ่นและนำกลับมาใช้ใหม่ได้ โดยการห่อหุ้มตรรกะและส่งต่อไปยังคอมโพเนนต์ผ่าน render prop คุณสามารถส่งเสริมการใช้โค้ดซ้ำ การประกอบคอมโพเนนต์ และการแยกส่วนความรับผิดชอบได้ แม้ว่า Hooks จะเป็นทางเลือกที่ทันสมัยและมักจะเรียบง่ายกว่า แต่ Render Props ยังคงเป็นเครื่องมือที่ทรงพลังในคลังแสงของนักพัฒนา React โดยเฉพาะอย่างยิ่งเมื่อต้องจัดการกับโค้ดเดิมหรือสถานการณ์ที่ต้องการการควบคุมกระบวนการแสดงผลอย่างละเอียด

ด้วยความเข้าใจในประโยชน์และแนวปฏิบัติที่ดีที่สุดของรูปแบบ Render Props คุณสามารถสร้างแอปพลิเคชันที่แข็งแกร่งและปรับเปลี่ยนได้ซึ่งรองรับผู้ชมทั่วโลกที่หลากหลาย ทำให้มั่นใจได้ว่าผู้ใช้จะได้รับประสบการณ์ที่สอดคล้องและน่าดึงดูดใจในภูมิภาคและวัฒนธรรมต่างๆ สิ่งสำคัญคือการเลือกรูปแบบที่เหมาะสม ไม่ว่าจะเป็น Render Props, HOCs หรือ Hooks โดยพิจารณาจากความต้องการเฉพาะของโครงการและความเชี่ยวชาญของทีมของคุณ โปรดจำไว้เสมอว่าต้องให้ความสำคัญกับความสามารถในการอ่าน การบำรุงรักษา และประสิทธิภาพของโค้ดเมื่อทำการตัดสินใจด้านสถาปัตยกรรม