Skip to main content

บันทึกการใช้งาน useRef createRef และ forwardRef ของ React

Kongvut Sangkla

Intro

สวัสดีครับ บทความนี้จะเป็นการบบันทึกการใช้งาน useRef createRef และ forwardRef ของ React โดย useRef และ createRef มีความคล้ายคลึงกันมากและแตกต่างกันอย่างไรมาดูกัน 🚀

TL; DR;

  • useRef คือ React Hooks ที่สามารถเข้าถึงโดยการ Reference ไปที่ DOM element ใช้กับ Functional component สามารถทำ initialValue (.current) ได้
  • createRef คือ วิธีการ Access DOM nodes ด้วยการ Reference ใช้กับ Class component (การทำงานคล้ายกับ useRef)
  • forwardRef คือ การส่งต่อ Ref จาก Parent ไปที่ Child เมื่อสร้างเป็น Ref เป็น Component

ตัวอย่างการใช้งาน useRef

Live Editor
() => {
  const inputRef = useRef("")
  const [value, setValue] = useState("")
  
  return (
    <>
      ดึงค่าจาก Element: {value}
      <hr />
      <input ref={inputRef} type="text" placeholder="Enter text ..." />
      <button onClick={() => {
        setValue(inputRef.current.value)
      }}>Value</button>
    </>
  )
}
Result
Loading...
Live Editor
() => {
  const divRef = useRef("")
  const [value, setValue] = useState("")
  
  return (
    <>
      ดึงค่าจาก Element: {value}
      <hr />
      <div ref={divRef} data="some value">Some content</div>
      <button onClick={() => {
        const el = divRef.current

        // <div text="some value">Some value</div>
        console.log(el)

        // some vale
        console.log(el.attributes.data.value)

        // Some content
        console.log(el.innerText)

        el.innerText = "Hello World!"

        setValue(el.innerText)
      }}>Value</button>
    </>
  )
}
Result
Loading...

ความเหมือนกันระหว่าง useRef และ createRef

ถ้าดูแค่ผิวเผินอาจจะดูความแตกต่างของผลลัพธ์ไม่ออกเลย เพราะทั้งสอง Component นั้นให้ผลลัพธ์เหมือนกัน

Live Editor
() => {
  const TextInputWithSelectTextButton1 = () => {
    const inputEl = useRef(null)
    const onButtonClick = () => {
      inputEl.current.select()
    };
    return (
      <>
        <input ref={inputEl} type="text" placeholder="Enter text ..." />
        <button onClick={onButtonClick}>Select Text</button>
      </>
    );
  }

  const TextInputWithSelectTextButton2 = () => {
    const inputRef = createRef()
    const onButtonClick = () => {
      inputRef.current.select()
    };
    return (
      <>
        <input ref={inputRef} type="text" placeholder="Enter text ..." />
        <button onClick={onButtonClick}>Select Text</button>
      </>
    );
  }
  
  return (
    <>
      <TextInputWithSelectTextButton1 />
      <hr />
      <TextInputWithSelectTextButton2 />
    </>
  )
}
Result
Loading...

ความแตกต่างระหว่าง useRef และ createRef

Live Editor
() => {
  const [renderIndex, setRenderIndex] = useState(1)
  const refFromUseRef = useRef(null)
  const refFromCreateRef = createRef()
  
  if (!refFromUseRef.current) {
    refFromUseRef.current = renderIndex;
  }

  if (!refFromCreateRef.current) {
    refFromCreateRef.current = renderIndex;
  }

  return (
    <>
      Current render index: {renderIndex}
      <br />
      First render index remembered within refFromUseRef.current:
      {refFromUseRef.current}
      <br />
      refFromCreateRef.current = Current render index:
      {refFromCreateRef.current}
      <br />
      <button onClick={() => setRenderIndex((prevState) => prevState + 1)}>
        Cause re-render
      </button>
    </>
  )
}
Result
Loading...
Live Editor
() => {
  const valueRef = React.useRef(100)
  return (
    <>
      เมื่อต้องการเปลี่ยนแปลงค่า Ref ให้อัปเดตค่า current<br />
      แต่ตัวอย่างนี้ค่า current จะไม่เปลี่ยนแปลง<br />
      ต้องใช้วิธีบางอย่างให้ทริกเกอร์ Re-Render Compoent ตามตัวอย่างถัดไป<hr />
      Value: {valueRef.current}<br />
      <button onClick={() => {
        valueRef.current = 150
      }}>Increase</button>
    </>
  )
}
Result
Loading...
Live Editor
() => {
  const valueRef = React.useRef(100)
  const [, setState] = useState(0)
  return (
    <>
      ใช้ Hooks useState เพื่ออัปเดต อะไรบางอย่างเข้ามาช่วยเพื่อ Re-Render<br />
      ก็จะสามารถทำให้ค่าใน current เปลี่ยน<hr />
      Value: {valueRef.current}<br />
      <button onClick={() => {
        valueRef.current = 150
        setState(1)
      }}>Increase</button>
    </>
  )
}
Result
Loading...
Live Editor
() => {
  const valueRef = React.createRef()
  const [, setState] = useState(0)
  return (
    <>
      แม้ว่า createRef ควรใช้กับ Class component<br />
      แต่ก็ใช้กับ Functional component ได้เช่นกัน<br />
      แต่เมื่อ Component มีการอัปเดต App ก็จะถูก Re-Render ใหม่<br />
      createRef ก็จะเริ่มต้น Reference ใหม่ทั้งหมด<br />
      ดังนั้นทำให้การอัปเดตค่า current ไม่มีผล<br />
      เป็นเหตุผลที่ควรใช้ Hooks useRef แทน<hr />
      Value: {valueRef.current}<br />
      <button onClick={() => {
        valueRef.current = 150
        setState(1)
      }}>Increase</button>
    </>
  )
}
Result
Loading...

การใช้งาน forwardRef

forwardRef คือ การส่งต่อ Ref จาก Parent ไปที่ Child เมื่อสร้างเป็น Ref เป็น Component (Custom)

Live Editor
() => {
  const ref = React.useRef(null);

  const alertText = () => {
    const input = ref.current
    if (input.value) alert(input.value)
    else input.focus()
  }

  const InputText = React.forwardRef((props, ref) => (
    <input ref={ref} {...props} />
  ))

  return (
    <>
      <InputText ref={ref} placeholder="Enter text..." />
      <button onClick={alertText}>Focus</button>
    </>
  )
}
Result
Loading...

สรุป

ใช้ useRef

  • ใช้กับ Functional component
  • สามารถกำหนด initialValue (.current) ได้
  • เพื่อรักษาค่า Ref ปัจจุบันใช้อยู่ตลอดการใช้งานของ Functional component และเมื่อมีการ Re-Render จะไม่สร้างค่า Ref ขึ้นมาใหม่
  • ความสามารถในการทำ Memorize
  • อัปเดตค่า .current ได้ (แต่ต้องสั่ง Trigger อะไรบางอย่างให้ Re-Render จากนั้นค่าที่กำหนดถึงจะเปลี่ยนแปลง)

ใช้ createRef

  • ใช้กับ Class component
  • เมื่อต้องการรีเช็ต Ref ใหม่ทุกครั้งที่มีการ Re-Render

ใช้ forwardRef เมื่อต้องการส่งต่อ Ref จาก Parent ไปที่ Child เมื่อสร้างเป็น Ref เป็น Component

อ้างอิงและรายละเอียดอื่น ๆ

Loading...