React Hooks: Die tiefen Schnitte

Avatar of Blessing Ene Anyebe
Blessing Ene Anyebe am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

Hooks sind wiederverwendbare Funktionen. Sie ermöglichen es Ihnen, Zustand und andere Funktionen (z. B. Lifecycle-Methoden usw.) zu verwenden, ohne eine Klasse schreiben zu müssen. Hook-Funktionen ermöglichen es uns, in den React-Zustandslebenszyklus einzusteigen, indem wir funktionale Komponenten verwenden. So können wir den Zustand unserer funktionalen Komponenten manipulieren, ohne sie in Klassenkomponenten umwandeln zu müssen.

React führte Hooks in Version 16.8 ein und fügt seitdem weitere hinzu. Einige sind nützlicher und beliebter als andere, wie die Hooks useEffect, useState und useContext. Ich habe keinen Zweifel daran, dass Sie sich auf diese verlassen haben, wenn Sie mit React arbeiten.

Aber was mich interessiert, sind die weniger bekannten React-Hooks. Während alle React-Hooks auf ihre Weise interessant sind, gibt es fünf, die ich Ihnen wirklich zeigen möchte, weil sie Ihnen im täglichen Gebrauch vielleicht nicht begegnen – oder vielleicht doch, und das Wissen darum verleiht Ihnen zusätzliche Superkräfte.

useReducer

Der Hook useReducer ist ein Zustandsverwaltungswerkzeug wie andere Hooks. Insbesondere ist er eine Alternative zum Hook useState.

Wenn Sie den Hook useReducer verwenden, um zwei oder mehr Zustände (oder Aktionen) zu ändern, müssen Sie diese Zustände nicht einzeln manipulieren. Der Hook verfolgt alle Zustände und verwaltet sie gemeinsam. Mit anderen Worten: Er verwaltet und rendert Zustandsänderungen neu. Im Gegensatz zum Hook useState ist useReducer einfacher, wenn es um die Verwaltung vieler Zustände in komplexen Projekten geht.

Anwendungsfälle

useReducer kann helfen, die Komplexität der Arbeit mit mehreren Zuständen zu reduzieren. Verwenden Sie ihn, wenn Sie mehrere Zustände gemeinsam verfolgen müssen, da er es Ihnen ermöglicht, die Zustandsverwaltung und die Rendering-Logik einer Komponente als separate Belange zu behandeln.

Syntax

useReducer akzeptiert drei Argumente, von denen eines optional ist

  • eine Reducer-Funktion
  • initialState
  • eine init-Funktion (optional)
const [state, dispatch] = useReducer(reducer, initialState)
const [state, dispatch] = useReducer(reducer, initialState initFunction) // in the case where you initialize with the optional 3rd argument

Beispiel

Das folgende Beispiel ist eine Oberfläche, die ein Texteingabefeld, einen Zähler und einen Button enthält. Die Interaktion mit jedem Element aktualisiert den Zustand. Beachten Sie, wie useReducer es uns ermöglicht, mehrere Fälle auf einmal zu definieren, anstatt sie einzeln einzurichten.

import { useReducer } from 'react';
const reducer = (state, action) => {
  switch (action.type) {
    case 'INCREMENT':
      return { ...state, count: state.count + 1 };
    case 'DECREMENT':
      return { ...state, count: state.count - 1 };
    case 'USER_INPUT':
      return { ...state, userInput: action.payload };
    case 'TOGGLE_COLOR':
      return { ...state, color: !state.color };
    default:
      throw new Error();
  }
}

function App() {
  const [state, dispatch] = useReducer(reducer, { count: 0, userInput: '', color: false })

  return (
    <main className="App, App-header" style={{ color: state.color ? '#000' : '#FF07FF'}}>
      <input style={{margin: '2rem'}}
        type="text"
        value={state.userInput}
        onChange={(e) => dispatch({ type: 'USER_INPUT', payload: e.target.value })}
      />
      <br /><br />
      <p style={{margin: '2rem'}} >{state.count}</p>
      <section style={{margin: '2rem'}}>
        <button  onClick={(() => dispatch({ type: 'DECREMENT' }))}>-</button>
        <button onClick={(() => dispatch({ type: 'INCREMENT' }))}>+</button>
        <button onClick={(() => dispatch({ type: 'TOGGLE_COLOR' }))}>Color</button>
      </section>
      <br /><br />
      <p style={{margin: '2rem'}}>{state.userInput}</p>
    </main>
  );
}
export default App;

Aus dem obigen Code sehen Sie, wie wir mehrere Zustände einfach im Reducer (switch-case) verwalten können. Dies zeigt den Vorteil von useReducer. Das ist die Macht, die er uns gibt, wenn wir in komplexen Anwendungen mit mehreren Zuständen arbeiten.

useRef

Der Hook useRef wird verwendet, um Refs auf Elemente zu erstellen, um auf das DOM zuzugreifen. Aber mehr noch, er gibt ein Objekt mit einer .current-Eigenschaft zurück, die während des gesamten Lebenszyklus einer Komponente verwendet werden kann. So bleiben Daten erhalten, ohne einen erneuten Render auszulösen. Der Wert von useRef bleibt also zwischen den Renderings gleich; die Aktualisierung der Referenz löst keinen erneuten Render aus.

Anwendungsfälle

Greifen Sie zum Hook useRef, wenn Sie

  • das DOM mit gespeicherten veränderlichen Informationen manipulieren möchten.
  • auf Informationen aus untergeordneten Komponenten (verschachtelte Elemente) zugreifen möchten.
  • den Fokus auf ein Element setzen möchten.

Er ist am nützlichsten, wenn Sie veränderliche Daten in Ihrer App speichern möchten, ohne einen erneuten Render auszulösen.

Syntax

useRef akzeptiert nur ein Argument, nämlich den Anfangswert.

const newRefComponent = useRef(initialValue);

Beispiel

Hier habe ich den Hook useRef und useState verwendet, um zu zeigen, wie oft eine Anwendung einen aktualisierten Zustand rendert, wenn in ein Texteingabefeld getippt wird.

import './App.css'

function App() {
  const [anyInput, setAnyInput] = useState(" ");
  const showRender = useRef(0);
  const randomInput = useRef();
  const toggleChange = (e) => {
    setAnyInput (e.target.value);
    showRender.current++;
  
  }
  const focusRandomInput = () => {
    randomInput.current.focus();
  }

  return (
    <div className="App">
      <input className="TextBox" 
        ref ={randomInput} type="text" value={anyInput} onChange={toggleChange}
      />
      <h3>Amount Of Renders: {showRender.current}</h3>
      <button onClick={focusRandomInput}>Click To Focus On Input </button>
    </div>
  );
}

export default App;

Beachten Sie, wie jeder Buchstabe, der in das Textfeld eingegeben wird, den Zustand der App aktualisiert, aber niemals einen vollständigen erneuten Render auslöst.

useImperativeHandle

Sie wissen, wie eine untergeordnete Komponente Funktionen aufrufen kann, die von der übergeordneten Komponente an sie übergeben wurden? Übergeordnete Komponenten geben diese über Props weiter, aber diese Weitergabe ist "unidirektional" in dem Sinne, dass die übergeordnete Komponente keine Funktion aufrufen kann, die sich in der untergeordneten Komponente befindet.

Nun, useImperativeHandle ermöglicht es einer übergeordneten Komponente, auf Funktionen einer untergeordneten Komponente zuzugreifen.

Wie funktioniert das?

  • Eine Funktion wird in der untergeordneten Komponente definiert.
  • In der übergeordneten Komponente wird ein ref hinzugefügt.
  • Wir verwenden forwardRef, wodurch der definierte ref an die untergeordnete Komponente weitergegeben werden kann.
  • useImperativeHandle stellt die Funktionen der untergeordneten Komponente über den ref bereit.

Anwendungsfälle

useImperativeHandle funktioniert gut, wenn Sie möchten, dass eine übergeordnete Komponente von Änderungen in der untergeordneten Komponente beeinflusst wird. Dinge wie ein geänderter Fokus, Inkrementieren und Dekrementieren sowie ausgeblendete Elemente können Situationen sein, in denen Sie diesen Hook verwenden, damit die übergeordnete Komponente entsprechend aktualisiert werden kann.

Syntax

useImperativeHandle (ref, createHandle, [dependencies])

Beispiel

In diesem Beispiel haben wir zwei Buttons, einen in einer übergeordneten Komponente und einen in einer untergeordneten. Das Klicken auf den übergeordneten Button ruft Daten aus der untergeordneten Komponente ab und ermöglicht uns, die übergeordnete Komponente zu manipulieren. Es ist so eingerichtet, dass das Klicken auf den untergeordneten Button nichts von der übergeordneten Komponente an die untergeordnete Komponente weitergibt, um zu veranschaulichen, wie wir Dinge in die entgegengesetzte Richtung übergeben.

// Parent component
import React, { useRef } from "react";
import ChildComponent from "./childComponent";
import './App.css';

function useImperativeHandle() {
  const controlRef = useRef(null);
  return (
    onClick={
      () => {
        controlRef.current.controlPrint();
      }
    }
    >
    Parent Box
  );
}
export default useImperativeHandle;
// Child component
import React, { forwardRef, useImperativeHandle, useState } from "react";

const ChildComponent = forwardRef((props, ref) => {
  const [print, setPrint] = useState(false);
  useImperativeHandle(ref, () => ({
    controlPrint() 
    { setPrint(!print); },
  })
  );

  return (
    <>
    Child Box
    { print && I am from the child component }
  );
});

export default ChildComponent;

Ausgabe

useMemo

useMemo ist einer der am seltensten verwendeten, aber interessantesten React-Hooks. Er kann die Leistung verbessern und die Latenz verringern, insbesondere bei großen Berechnungen in Ihrer App. Wie das geht? Jedes Mal, wenn sich der Zustand einer Komponente aktualisiert und Komponenten neu gerendert werden, verhindert der Hook useMemo, dass React Werte neu berechnen muss.

Sehen Sie, Funktionen reagieren auf Zustandsänderungen. Der Hook useMemo nimmt eine Funktion und **gibt den Rückgabewert dieser Funktion zurück**. Er speichert diesen Wert zwischen, um zu verhindern, dass zusätzliche Anstrengungen für die Neudefinition aufgewendet werden, und gibt ihn dann zurück, wenn sich eine der Abhängigkeiten geändert hat.

Dieser Prozess wird als Memoisation bezeichnet und hilft, die Leistung zu steigern, indem der Wert aus einer früheren Anfrage gespeichert wird, damit er wiederverwendet werden kann, ohne all die Berechnungen zu wiederholen.

Anwendungsfälle

Die besten Anwendungsfälle sind immer dann, wenn Sie mit rechenintensiven Berechnungen arbeiten, bei denen Sie den Wert speichern und bei nachfolgenden Zustandsänderungen verwenden möchten. Es kann ein schöner Leistungsgewinn sein, aber zu viel davon zu verwenden, kann genau den gegenteiligen Effekt haben, indem es den Speicher Ihrer App belastet.

Syntax

useMemo( () => 
  { // Code goes here },
  []
)

Beispiel

Beim Klicken auf den Button zeigt dieses Mini-Programm an, ob eine Zahl gerade oder ungerade ist, und quadriert dann den Wert. Ich habe viele Nullen zur Schleife hinzugefügt, um ihre Rechenleistung zu erhöhen. Sie gibt den Wert in Sekundenschnelle zurück und funktioniert dank des Hooks useMemo immer noch gut.

// UseMemo.js
import React, { useState, useMemo } from 'react'

function Memo() {
  const [memoOne, setMemoOne] = useState(0);
  const incrementMemoOne = () => { setMemoOne(memoOne + 1) }
  const isEven = useMemo(() => { 
    let i = 0 while (i < 2000000000) i++ return memoOne % 2 === 0
  },
  [memoOne]);
  
  const square = useMemo(()=> { 
    console.log("squared the number"); for(var i=0; i < 200000000; i++);
    return memoOne * memoOne;
  },
  [memoOne]);

  return (
    Memo One - 
    { memoOne }
    { isEven ? 'Even' : 'Odd' } { square } 
  );
}
export default Memo

Ausgabe

useMemo ist ein wenig wie der Hook useCallback, aber der Unterschied ist, dass useMemo einen gemerzeten Wert aus einer Funktion speichern kann, während useCallback die gemerzete Funktion selbst speichert.

useCallback

Der Hook useCallback ist ein weiterer interessanter Hook, und der letzte Abschnitt war eine Art Spoiler-Alarm für das, was er tut.

Wie wir gerade gesehen haben, funktioniert useCallback wie der Hook useMemo, da beide Memoisation verwenden, um etwas für die spätere Verwendung zu cachen. Während useMemo die Berechnung einer Funktion als gecachten Wert speichert, speichert und gibt useCallback eine Funktion zurück.

Anwendungsfälle

Wie useMemo ist useCallback eine schöne Leistungsoptimierung, da es einen gemerzeten Callback und seine Abhängigkeiten ohne erneuten Render speichert und zurückgibt.

Syntax

const getMemoizedCallback = useCallback (
  () => { doSomething () }, []
);

Beispiel


{ useCallback, useState } from "react";
import CallbackChild from "./UseCallback-Child";
import "./App.css"

export default function App() {
  const [toggle, setToggle] = useState(false);
  const [data, setData] = useState("I am a data that would not change at every render, thanks to the useCallback");
  const returnFunction = useCallback(
    (name) => 
    { return data + name; }, [data]
  );
  return (
    onClick={() => {
      setToggle(!toggle);
    }}
    >
    {" "}

    // Click To Toggle
    { toggle && h1. Toggling me no longer affects any function } 
  ); 
}
// The Child component
import React, { useEffect } from "react";

function CallbackChild(
  { returnFunction }
) {
  useEffect(() => 
    { console.log("FUNCTION WAS CALLED"); },
    [returnFunction]);
  return { returnFunction(" Hook!") };
}
export default CallbackChild;

Ausgabe

Abschließende Gedanken

Da haben wir's! Wir haben uns fünf super praktische React-Hooks angesehen, die meiner Meinung nach oft übersehen werden. Wie bei vielen Zusammenfassungen dieser Art kratzen wir hier nur an der Oberfläche dieser Hooks. Jeder von ihnen hat seine eigenen Nuancen und Überlegungen, die man bei der Verwendung berücksichtigen muss. Aber hoffentlich haben Sie eine gute High-Level-Vorstellung davon, was sie sind und wann sie besser passen könnten als ein anderer Hook, zu dem Sie vielleicht öfter greifen.

Der beste Weg, sie vollständig zu verstehen, ist durch Übung. Deshalb ermutige ich Sie, diese Hooks in Ihrer Anwendung zu üben, um sie besser zu verstehen. Dazu können Sie tiefer eintauchen, indem Sie die folgenden Ressourcen konsultieren