Refs zum Ablegen von Objektreferenzen

Refs zum Ablegen von Objektreferenzen

refs: ähnlich zu State, mit Unterschieden:

  • Änderung eines Refs führt nicht zu einem Re-Rendering
  • Der Wert eines Refs ist nie veraltet

Refs zum Ablegen von Objektreferenzen

erster Use-Case:

Ablegen von Werten / Objektreferenzen, die das Rendering einer Komponente nicht beeinflussen:

  • Timer IDs
  • Request IDs
  • WebSocket-Verbindungen
  • ein PWA-Installationsdialog (siehe Abschnitt PWA)
  • DOM-Elemente (siehe nächster Abschnitt "Ref Property zum Zugriff auf DOM-Elemente")

Refs zum Ablegen von Objektreferenzen

zweiter Use-Case:

Speichern der aktuellsten Version eines States

Refs zum Ablegen von Objektreferenzen

Auf eine Ref kann mittels useRef zugegriffen werden

Objekte werden in der .current-Property der Ref abgelegt

Refs zum Ablegen von Objektreferenzen

ein Beispiel mit beiden Use-Cases:

function StopWatch() {
  const [time, setTime] = useState(0);
  const timeRef = useRef(time);
  const intervalId = useRef<number>(null);
  function start() {
    setTime(0);
    timeRef.current = 0;
    intervalId.current = setInterval(() => {
      timeRef.current++;
      setTime(timeRef.current);
    }, 1000);
  }
  function stop() {
    clearInterval(intervalId.current);
  }
  return <div>...</div>;
}

Refs zum Ablegen von Objektreferenzen

Bemerkung: oft gibt es auch mögliche Implementierungen, die Refs vermeiden:

function StopWatch() {
  const [time, setTime] = useState(0);
  const [running, setRunning] = useState(false);
  useEffect(() => {
    if (running) {
      const intervalId = setInterval(() => {
        setTime((time) => time + 1);
      }, 1000);
      return () => clearInterval(intervalId);
    }
  }, [running]);
  return <div>...</div>;
}

Ref-Property zum Zugriff auf DOM-Elemente

Ref-Property zum Zugriff auf DOM-Elemente

Ähnlich wie key hat auch die ref-Property eine besondere Bedeutung in JSX - sie ermöglicht den Zugriff auf gerenderte DOM-Elemente

Anwendungsgebiete:

  • Verwalten von Fokus, Textauswahl oder Medienwiedergabe
  • Integration von anderen DOM-Libraries
  • Alternative Möglichkeit zum Verwalten von Inputs

Ref-Property zum Zugriff auf DOM-Elemente

Verwendung der ref-Property zusammen mit dem useRef-Hook:

function RefDemo() {
  const myRef = useRef<HTMLInputElement>(null);
  return (
    <input
      ref={myRef}
      onChange={() => {
        console.log(myRef.current.value);
      }}
    />
  );
}

Ref-Property zum Zugriff auf DOM-Elemente

Verwalten von Fokus, Textauswahl oder Medienwiedergabe

manche Änderungen können nicht deklarativ (via State) ausgedrückt werden - sie benötigen Zugriff zu einem bestimmten DOM-Element

Beispiel: es gibt Properties wie .value zum Ändern des Werts eines Inputs oder .className zum Ändern von Klassennamen, aber keine Property zum Verwalten des Fokus

Ref-Property zum Zugriff auf DOM-Elemente

Verwalten des Fokus:

function App() {
  const inputEl = useRef<HTMLInputElement>(null);
  useEffect(() => {
    inputEl.current?.focus();
  }, []);
  return <input ref={inputEl} />;
};

Ref-Property zum Zugriff auf DOM-Elemente

Verwalten von Medienwiedergabe:

// https://codesandbox.io/s/media-playback-x3ci4
function Video() {
  const [playing, setPlaying] = useState(false);
  const videoEl = useRef<HTMLVideoElement>(null);
  useEffect(() => {
    if (playing) {
      videoEl.current?.play();
    } else {
      videoEl.current?.pause();
    }
  }, [playing]);
  return (
    <div>
      <video
        ref={videoEl}
        width="250"
        src={
          'https://interactive-examples.mdn.mozilla.net/' +
          'media/cc0-videos/flower.mp4'
        }
      />
      <button onClick={() => setPlaying(!playing)}>
        {playing ? 'pause' : 'play'}
      </button>
    </div>
  );
}

Ref-Property zum Zugriff auf DOM-Elemente

Integration von anderen DOM-Libraries

Andere Libraries können expliziten Zugriff auf DOM-Elemente benötigen

Beispiel: Die Google Maps Library nimmt ein DOM-Element entgegen, in dem die Karte gezeichnet wird

Für viele Libraries (so auch Google Maps) existieren Wrapper für React, sodass refs nicht benötigt werden

Ref-Property zum Zugriff auf DOM-Elemente

Integration von Google Maps mittels Ref:

function Map() {
  const mapRef = useRef<HTMLElement>();
  useEffect(() => {
    new google.maps.Map(mapRef.current);
  }, []);
  return <div ref={mapRef} style={height: 400} />;
}

Ref-Property zum Zugriff auf DOM-Elemente

Alternative Möglichkeit zum Verwalten von Inputs

Verwendung von ref Anstelle von value und onChange kann zu etwas kürzerem Code führen (wird aber in der Dokumentation nicht empfohlen)

Refs werden von react-hook-form verwendet, um Formularverwaltung einfacher und schneller zu machen

Ref-Property zum Verwalten von Inputs

Verwalten von Inputs: Vergleich von useState und useRef:

const App = () => {
  const [firstName, setFirstName] = useState('');
  const lastNameInput = useRef<HTMLInputElement>(null);

  return (
    <div>
      <input
        value={firstName}
        onChange={(event) =>
          setFirstName(event.target.value)
        }
      />
      <input ref={lastNameInput} />

      <button
        onClick={() => {
          console.log(firstName);
          console.log(lastNameInput.current.value);
        }}
      >
        log values
      </button>
    </div>
  );
};

Callback Refs

Wie bisher gesehen: Wir können ein Ref-Objekt an die ref-Property übergeben

Wir können auch eine Callbackfunktion übergeben, die nach dem Rendering mit dem Element als Parameter aufgerufen wird (react-hook-form verwendet dies)

<input
  ref={(element) => {
    console.log(element);
    console.log(element.value);
    element.focus();
  }}
/>

Ref-Property in eigenen Komponenten

Ref-Property in eigenen Komponenten

Beispiel: TextField-Komponente in MUI

const containerRef = useRef<HTMLDivElement>(null);
const inputRef = useRef<HTMLInputElement>(null);
<TextField ref={containerRef} inputRef={inputRef} />
  • ref: kann verwendet werden, um auf das äußere <div> zuzugreifen
  • inputRef: kann verwendet werden, um auf das <input>-Element zuzugreifen

Ref-Property in eigenen Komponenten

Implementierung einer Ref, deren Property-Name nicht "ref" ist:

type Props = {
  inputRef: RefObject<HTMLInputElement>;
  // ...
};

function TextField(props: Props) {
  return (
    <div>
      ...
      <input ref={props.inputRef} />
    </div>
  );
}

Ref-Property in eigenen Komponenten

Implementierung einer Ref, deren Property-Name "ref" ist:

type Props = {
  // ...
};

const TextField = forwardRef<HTMLDivElement, Props>(
  (props, ref) => {
    return (
      <div ref={ref}>
        ...
        <input />
      </div>
    );
  }
);

Ref-Property in eigenen Komponenten

Kombination der Implementierungen:

type Props = {
  inputRef: RefObject<HTMLInputElement>;
  // ...
};

const TextField = forwardRef<HTMLDivElement, Props>(
  (props, ref) => {
    return (
      <div ref={ref}>
        ...
        <input ref={props.inputRef} />
      </div>
    );
  }
);