ความแตกต่างระหว่างʻuseRef` และ "createRef" คืออะไร?


102

ผมจะผ่านเอกสารตะขอเมื่อฉัน stumbled useRefเมื่อ

ดูตัวอย่างของพวกเขา ...

function TextInputWithFocusButton() {
  const inputEl = useRef(null);
  const onButtonClick = () => {
    // `current` points to the mounted text input element
    inputEl.current.focus();
  };
  return (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </>
  );
}

... มันดูเหมือนว่าจะถูกแทนที่ด้วยuseRefcreateRef

function TextInputWithFocusButton() {
  const inputRef = createRef(); // what's the diff?
  const onButtonClick = () => {
    // `current` points to the mounted text input element
    inputRef.current.focus();
  };
  return (
    <>
      <input ref={inputRef} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </>
  );
}

เหตุใดฉันจึงต้องมีตะขอสำหรับอ้างอิง ทำไมถึงuseRefมีอยู่?

คำตอบ:


130

ความแตกต่างคือcreateRefจะสร้างการอ้างอิงใหม่เสมอ ในองค์ประกอบตามคลาสโดยทั่วไปคุณจะใส่ ref ในคุณสมบัติอินสแตนซ์ระหว่างการก่อสร้าง (เช่นthis.input = createRef()) คุณไม่มีตัวเลือกนี้ในส่วนประกอบของฟังก์ชัน useRefดูแลการส่งคืนการอ้างอิงเดียวกันทุกครั้งเช่นเดียวกับการแสดงผลครั้งแรก

นี่คือตัวอย่างแอพที่แสดงให้เห็นถึงความแตกต่างในลักษณะการทำงานของฟังก์ชันทั้งสองนี้:

import React, { useRef, createRef, useState } from "react";
import ReactDOM from "react-dom";

function App() {
  const [renderIndex, setRenderIndex] = useState(1);
  const refFromUseRef = useRef();
  const refFromCreateRef = createRef();
  if (!refFromUseRef.current) {
    refFromUseRef.current = renderIndex;
  }
  if (!refFromCreateRef.current) {
    refFromCreateRef.current = renderIndex;
  }
  return (
    <div className="App">
      Current render index: {renderIndex}
      <br />
      First render index remembered within refFromUseRef.current:
      {refFromUseRef.current}
      <br />
      First render index unsuccessfully remembered within
      refFromCreateRef.current:
      {refFromCreateRef.current}
      <br />
      <button onClick={() => setRenderIndex(prev => prev + 1)}>
        Cause re-render
      </button>
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

แก้ไข 1rvwnj71x3


34

createRefส่งคืนการอ้างอิงใหม่เสมอซึ่งโดยทั่วไปคุณจะเก็บเป็นฟิลด์ในอินสแตนซ์ของส่วนประกอบคลาส useRefส่งคืนการอ้างอิงเดียวกันในทุกการแสดงผลของอินสแตนซ์ของส่วนประกอบที่ใช้งานได้ นี่คือสิ่งที่ช่วยให้สถานะของการอ้างอิงยังคงอยู่ระหว่างการแสดงผลแม้ว่าคุณจะไม่ได้จัดเก็บไว้ที่ใดก็ตามอย่างชัดเจน

ในตัวอย่างที่สองของคุณการอ้างอิงจะถูกสร้างขึ้นใหม่ในทุกการเรนเดอร์


สิ่งนี้ไม่ถูกต้องคุณมีข้อมูลอ้างอิงเพื่อสำรองคำสั่งของคุณหรือไม่?
Adeel Imran

มีความคิดเห็นที่นี่โดยหนึ่งใน React devs อธิบายว่านี่คือวิธีการทำงาน: reddit.com/r/reactjs/comments/a2pt15/…ฉันสนใจที่จะรู้ว่าคุณคิดว่าอะไรไม่ถูกต้องเกี่ยวกับคำตอบนี้
Joe Clay

ฉันเห็นลิงค์นั้นก่อนที่จะพยายามตอบคำถามนี้มันระบุข้อเท็จจริงนี้ไว้ที่ใดในลิงก์ที่คุณแชร์ หาไม่เจอ? :)
Adeel Imran

1
ลิงก์ที่ฉันแชร์แสดงให้เห็นการใช้งานที่ง่ายขึ้นuseRefซึ่งโพสต์โดยหนึ่งในผู้พัฒนา React ไม่เหมือนกับการโทรเพียงอย่างcreateRefเดียวเนื่องจากcreateRefไม่ใช่การเชื่อมต่อและไม่คงอยู่ระหว่างการโทร คำตอบของ Ryan Cogswell ยังมีตัวอย่างที่ดีของความแตกต่าง
Joe Clay

1
ความเข้าใจของฉันจากบริบทนั้นอนุมานได้ว่า useRef เป็น hook แบบกำหนดเองที่ใช้ createRef ภายใน ขอบคุณที่แบ่งปันความรู้
Adeel Imran

5

tldr

เป็นวัตถุref JS ธรรมดา{ current: <some value> }

React.createRef()เป็นโรงงานที่กลับมาเตะ{ current: null }- มายากลไม่เกี่ยวข้องกับ

useRef(initValue)นอกจากนี้ยังส่งกลับโทษคล้ายกับ{ current: initValue } นอกจากนี้ก็memoizesโทษนี้จะเป็นแบบถาวรในหลายวาทกรรมในองค์ประกอบฟังก์ชั่นReact.createRef()

มันเพียงพอที่จะใช้React.createRefในองค์ประกอบคลาสเนื่องจากอ็อบเจ็กต์ ref ถูกกำหนดให้กับตัวแปรอินสแตนซ์ดังนั้นจึงสามารถเข้าถึงได้ตลอดทั้งส่วนประกอบและอายุการใช้งาน:
this.myRef = React.createRef(); // stores ref in "mutable" this context (class)

useRef(null)โดยทั่วไปจะเทียบเท่ากับ 1useState(React.createRef())[0]


1แทนที่useRefด้วยuseState+createRef

ทวีตต่อไปนี้ได้รับความกระจ่างสำหรับฉัน:

useRef()useState({current: initialValue })[0]เป็นพื้น

ด้วยข้อมูลเชิงลึกจากtldrส่วนนี้เราสามารถสรุปเพิ่มเติมได้:

useRef(null)useState(React.createRef())[0]เป็นพื้น

โค้ดข้างต้น "ละเมิดสิทธิมนุษยชน" ยังคงได้รับเตะกลับมาจากuseState เพียงแค่เลือกส่วนค่าของ- จะเป็นตัวตั้งค่าReact.createRef()[0]useState[1]

useStateuseRefทำให้เกิดใหม่แสดงผลในทางตรงกันข้ามกับ อย่างเป็นทางการมากขึ้น React จะเปรียบเทียบการอ้างอิงอ็อบเจ็กต์เก่าและใหม่สำหรับuseStateเมื่อมีการตั้งค่าใหม่โดยใช้วิธี setter ถ้าเรากลายพันธุ์ของรัฐuseStateโดยตรง (ตรงข้ามกับหมาภาวนา) พฤติกรรมของมันมากหรือน้อยจะกลายเป็นเทียบเท่าที่จะuseRefเป็นไม่มีอีกครั้งทำให้ถูกเรียกอีกต่อไป:

// Example of mutaing object contained in useState directly
const [ref] = useState({ current: null })
ref.current = 42; // doesn't cause re-render

หมายเหตุ: อย่าทำแบบนี้! ใช้useRefAPI ที่ปรับให้เหมาะสมแทนการสร้างล้อใหม่ ด้านบนมีวัตถุประสงค์เพื่อเป็นภาพประกอบ


3

เพียงเพื่อเน้นจุดประสงค์:

createRefreturn {current: null}เป็นง่ายๆเป็น เป็นวิธีจัดการกับref=เสาในวิธีที่ทันสมัยที่สุดและนั่นก็เป็นเช่นนั้น (ในขณะที่การใช้สตริงเป็นวิธีที่วิเศษเกินไปและการเรียกกลับตามก็ดูเวอร์เกินไป)

useRefเก็บข้อมูลบางส่วนก่อนที่จะแสดงผลและการเปลี่ยนแปลงจะไม่ทำให้เกิดการเรนเดอร์ซ้ำ (เช่นเดียวกับuseState) พวกเขาไม่ค่อยเกี่ยวข้องกัน ทุกสิ่งที่คุณคาดหวังสำหรับองค์ประกอบตามคลาสไปที่ช่องอินสแตนซ์ ( this.* =) ดูเหมือนว่าผู้สมัครที่จะนำไปใช้useRefในส่วนประกอบที่ใช้งานได้

กล่าวว่าuseCallbackการทำงานเป็นวิธีการเรียน จำกัด ( this.handleClick = .....bind(this)) และอาจจะนำมาใช้อีกครั้ง ( แต่เราไม่ควรอีกครั้งคิดค้นล้อสำหรับการตรวจสอบ) useRefด้วย

อีกตัวอย่างหนึ่ง ได้แก่ การอ้างอิง DOM รหัสการหมดเวลา / ช่วงเวลาตัวระบุหรือการอ้างอิงของไลบรารีของบุคคลที่สาม

PS ผมเชื่อว่าตอบสนองทีมที่ดีกว่าเลือกที่แตกต่างกันสำหรับการตั้งชื่อให้เกิดความสับสนกับการหลีกเลี่ยงuseRef createRefบางทีหรือแม้กระทั่งuseAndKeepusePermanent


1

อีกอย่างหนึ่ง แต่สำคัญเพิ่มเติมสำหรับคำตอบของผู้อื่น

createRefคุณไม่สามารถตั้งค่าใหม่สำหรับ useRefแต่คุณสามารถทำได้สำหรับ

const ur = useRef();
const cr = createRef();

ur.current = 10; // you can do it, and value is set
cr.current = 10; // you can, but it's no good, it will not change it

ref เป็นวัตถุธรรมดาคุณสามารถเปลี่ยนcurrentคุณสมบัติได้ตามปกติ (ทดสอบแค่นั้น) ไม่สำคัญว่าการอ้างอิงถูกสร้างขึ้นผ่านuseRefหรือcreateRef.
ford04
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.