ReactJS: Haken Spickzettel





Guten Tag, Freunde!



Hier ist eine Anleitung zu den wichtigsten React-Hooks: useState, useEffect, useLayoutEffect, useContext, useReducer, useCallback, useMemo und UseRef.



Inspiration: React Hooks Spickzettel: Schalte Lösungen für häufig auftretende Probleme frei .



Der Zweck dieses Handbuchs ist es, einen kurzen Überblick über den Zweck und die Fähigkeiten jedes Hakens zu geben. Nach der Beschreibung des Hakens gibt es einen Beispielcode für seine Verwendung und eine Sandbox für Ihre Experimente.



Der vollständige Satz von Hooks ist in diesem Repository verfügbar .



  1. Repository herunterladen
  2. Abhängigkeiten installieren: npm i
  3. Run: npm start


Hooks befinden sich im Verzeichnis "hooks". Die Hauptdatei ist index.js. Um einen bestimmten Hook auszuführen, müssen Sie die entsprechenden Import- und Renderzeilen auskommentieren.



Ohne weiteres Vorwort.



useState



Mit useState können Sie mit dem Status von Variablen innerhalb einer Funktionskomponente arbeiten.



Variabler Zustand


Um den Status der Variablen zu ermitteln, rufen Sie useState mit dem Anfangsstatus als Argument auf: useState (initialValue).



const DeclareState = () => {
  const [count] = useState(1);
  return <div>  - {count}.</div>;
};


Aktualisieren des Status einer Variablen


Um den Status einer Variablen zu aktualisieren, rufen Sie die von useState zurückgegebene Aktualisierungsfunktion auf: const [state, updater] = useState (initialValue).



Der Code:



const UpdateState = () => {
  const [age, setAge] = useState(19);
  const handleClick = () => setAge(age + 1);

  return (
    <>
      <p> {age} .</p>
      <button onClick={handleClick}> !</button>
    </>
  );
};


Sandkasten:





Mehrere variable Zustände


In einer Funktionskomponente können Sie die Zustände mehrerer Variablen definieren und aktualisieren.



Der Code:



const MultStates = () => {
  const [age, setAge] = useState(19);
  const [num, setNum] = useState(1);

  const handleAge = () => setAge(age + 1);
  const handleNum = () => setNum(num + 1);

  return (
    <>
      <p> {age} .</p>
      <p>  {num}   .</p>
      <button onClick={handleAge}> !</button>
      <button onClick={handleNum}>   !</button>
    </>
  );
};


Sandkasten:





Verwenden eines Objekts zum Bestimmen des Status einer Variablen


Neben Zeichenfolgen und Zahlen können Objekte als Anfangswert verwendet werden. Beachten Sie, dass useStateUpdater das gesamte Objekt übergeben muss, da es ersetzt und nicht mit dem vorherigen zusammengeführt wird.



// setState ( ) - useState ( )
// ,    - {name: "Igor"}

setState({ age: 30 });
//   
// {name: "Igor", age: 30} -  

useStateUpdater({ age: 30 });
//   
// {age: 30} -   


Der Code:



const StateObject = () => {
  const [state, setState] = useState({ age: 19, num: 1 });
  const handleClick = (val) =>
    setState({
      ...state,
      [val]: state[val] + 1,
    });
  const { age, num } = state;

  return (
    <>
      <p> {age} .</p>
      <p>  {num}   .</p>
      <button onClick={() => handleClick('age')}> !</button>
      <button onClick={() => handleClick('num')}>   !</button>
    </>
  );


Sandkasten:





Initialisieren des Status einer Variablen mithilfe einer Funktion


Der Anfangswert des Zustands einer Variablen kann durch eine Funktion bestimmt werden.



const StateFun = () => {
  const [token] = useState(() => {
    const token = localStorage.getItem("token");
    return token || "default-token";
  });

  return <div> - {token}</div>;
};


Funktion statt setState


Die von useState zurückgegebene Aktualisierungsfunktion kann mehr als nur setState sein.



const [value, updateValue] = useState(0);
//    ,  ,  
updateValue(1);
updateValue((prevVal) => prevVal + 1);


Die zweite Methode eignet sich für Fälle, in denen die Aktualisierung vom vorherigen Status abhängt.



Der Code:



const CounterState = () => {
  const [count, setCount] = useState(0);

  return (
    <>
      <p>   {count}.</p>
      <button onClick={() => setCount(0)}></button>
      <button onClick={() => setCount((prevVal) => prevVal + 1)}>
         (+)
      </button>
      <button onClick={() => setCount((prevVal) => prevVal - 1)}>
         (-)
      </button>
    </>
  );
};


Sandkasten:





useEffect



useEffect akzeptiert eine Funktion, die für zusätzliche (Nebenwirkungen) verantwortlich ist.



Grundlegende Verwendung


Der Code:



const BasicEffect = () => {
  const [age, setAge] = useState(19);
  const handleClick = () => setAge(age + 1);

  useEffect(() => {
    document.title = ` ${age} !`;
  });

  return (
    <>
      <p>      .</p>
      <button onClick={handleClick}> !</button>
    </>
  );
};


Sandkasten:





Einen Effekt entfernen (abbrechen)


Es ist üblich, den Effekt nach einer Weile zu entfernen. Dies kann mithilfe der Funktion erfolgen, die von dem an useEffect übergebenen Effekt zurückgegeben wird. Unten sehen Sie ein Beispiel mit addEventListener.



Der Code:



const CleanupEffect = () => {
  useEffect(() => {
    const clicked = () => console.log("!");
    window.addEventListener("click", clicked);

    return () => {
      window.removeEventListener("click", clicked);
    };
  }, []);

  return (
    <>
      <p>        .</p>
    </>
  );
};


Sandkasten:





Mehrere Effekte


Multiple useEffects können in einer Funktionskomponente verwendet werden.



Der Code:



const MultEffects = () => {
  //   
  useEffect(() => {
    const clicked = () => console.log("!");
    window.addEventListener("click", clicked);

    return () => {
      window.removeEventListener("click", clicked);
    };
  }, []);

  //   
  useEffect(() => {
    console.log(" .");
  });

  return (
    <>
      <p>  .</p>
    </>
  );
};


Sandkasten:







Beachten Sie, dass der Aufruf von useEffect beim erneuten Rendern übersprungen werden kann, indem ein leeres Array als zweites Argument übergeben wird.



Effektabhängigkeiten


Der Code:



const EffectDependency = () => {
  const [randomInt, setRandomInt] = useState(0);
  const [effectLogs, setEffectLogs] = useState([]);
  const [count, setCount] = useState(1)

  useEffect(() => {
    setEffectLogs((prevEffectLogs) => [
      ...prevEffectLogs,
      `   ${count}.`,
    ]);
    setCount(count + 1)
  }, [randomInt]);

  return (
    <>
      <h3>{randomInt}</h3>
      <button onClick={() => setRandomInt(~~(Math.random() * 10))}>
           !
      </button>
      <ul>
        {effectLogs.map((effect, i) => (
          <li key={i}>{"  ".repeat(i) + effect}</li>
        ))}
      </ul>
    </>
  );
};


Sandkasten:







In diesem Fall übergeben wir die randomInt-Abhängigkeit an useEffect als zweites Argument, sodass die Funktion beim ersten Rendern sowie bei jeder Änderung von randomInt aufgerufen wird.



Effekt überspringen (leere Array-Abhängigkeit)


Im folgenden Beispiel wird useEffect als Abhängigkeit ein leeres Array übergeben, sodass der Effekt nur beim ersten Rendern funktioniert.



Der Code:



const SkipEffect = () => {
  const [randomInt, setRandomInt] = useState(0);
  const [effectLogs, setEffectLogs] = useState([]);
  const [count, setCount] = useState(1);

  useEffect(() => {
    setEffectLogs((prevEffectLogs) => [
      ...prevEffectLogs,
      `   ${count}.`,
    ]);
    setCount(count + 1);
  }, []);

  return (
    <>
      <h3>{randomInt}</h3>
      <button onClick={() => setRandomInt(~~(Math.random() * 10))}>
           !
      </button>
      <ul>
        {effectLogs.map((effect, i) => (
          <li key={i}>{"  ".repeat(i) + effect}</li>
        ))}
      </ul>
    </>
  );
};


Sandkasten:







Wenn Sie auf die Schaltfläche klicken, wird useEffect nicht aufgerufen.



Überspringender Effekt (keine Abhängigkeiten)


Wenn keine Abhängigkeiten vorhanden sind, wird der Effekt jedes Mal ausgelöst, wenn die Seite gerendert wird.



useEffect(() => {
  console.log(
    "        ."
  );
});


useContext



Mit useContext müssen Sie sich nicht mehr auf den Kontextkonsumenten verlassen. Es hat eine einfachere Oberfläche als MyContext.Consumer und rendert Requisiten. Im Folgenden finden Sie einen Vergleich der Verwendung von Kontext mit useContext und Context.Consumer.



//    Context
const ThemeContext = React.createContext("dark")

//   
function Button() {
    return (
        <ThemeContext.Consumer>
            {theme => <button className={thene}> !</button>}
        </ThemeContext.Consumer>
}

//  useContext
import { useContext } from "react"

function ButtonHook() {
    const theme = useContext(ThemeContext)
    return <button className={theme}> !</button>
}


Der Code:



const ChangeTheme = () => {
  const [mode, setMode] = useState("light");

  const handleClick = () => {
    setMode(mode === "light" ? "dark" : "light");
  };

  const ThemeContext = React.createContext(mode);

  const theme = useContext(ThemeContext);

  return (
    <div
      style={{
        background: theme === "light" ? "#eee" : "#222",
        color: theme === "light" ? "#222" : "#eee",
        display: "grid",
        placeItems: "center",
        minWidth: "320px",
        minHeight: "320px",
        borderRadius: "4px",
      }}
    >
      <p> : {theme}.</p>
      <button onClick={handleClick}>  </button>
    </div>
  );
};


Sandkasten:





useLayoutEffect



Das Verhalten von useLayoutEffect ähnelt dem von useEffect, mit wenigen Ausnahmen, über die wir später sprechen werden.



  useLayoutEffect(() => {
    // 
  }, []);


Grundlegende Verwendung


Hier ist ein Beispiel mit useEffect, aber mit useLayoutEffect.



Der Code:



  const [randomInt, setRandomInt] = useState(0);
  const [effectLogs, setEffectLogs] = useState([]);
  const [count, setCount] = useState(1);

  useLayoutEffect(() => {
    setEffectLogs((prevEffectLogs) => [
      ...prevEffectLogs,
      `   ${count}.`,
    ]);
    setCount(count + 1);
  }, [randomInt]);

  return (
    <>
      <h3>{randomInt}</h3>
      <button onClick={() => setRandomInt(~~(Math.random() * 10))}>
           !
      </button>
      <ul>
        {effectLogs.map((effect, i) => (
          <li key={i}>{"  ".repeat(i) + effect}</li>
        ))}
      </ul>
    </>
  );
};


Sandkasten:





useLayoutEffect und useEffect


Die an useEffect übergebene Funktion wird aufgerufen, nachdem die Seite gerendert wurde, d. H. nach dem Erstellen des Layouts und Zeichnen der Elemente Dies ist für die meisten zusätzlichen Effekte geeignet, die den Fluss nicht blockieren sollten. Wenn Sie jedoch als zusätzlichen Effekt eine DOM-Manipulation durchführen möchten, ist useEffect nicht die beste Wahl. Um zu verhindern, dass der Benutzer die Änderungen sieht, sollte useLayoutEffect verwendet werden. Die von useLayoutEffect übergebene Funktion wird aufgerufen, bevor die Seite gerendert wird.



useReducer



useReducer kann als Alternative zu useState verwendet werden. Sein Zweck besteht jedoch darin, komplexe Logik für die Arbeit mit Zuständen zu kapseln, wenn der Zustand vom vorherigen Wert abhängt oder mehrere Zustände vorliegen.



Grundlegende Verwendung


Im folgenden Beispiel wird useReducer anstelle von useState verwendet. Der Aufruf useReducer gibt einen Statuswert und eine Versandfunktion zurück.



Der Code:



const initialState = { width: 30 };

const reducer = (state, action) => {
  switch (action) {
    case "plus":
      return { width: Math.min(state.width + 30, 600) };
    case "minus":
      return { width: Math.max(state.width - 30, 30) };
    default:
      throw new Error(" ?");
  }
};

const BasicReducer = () => {
  const [state, dispath] = useReducer(reducer, initialState);
  const color = `#${((Math.random() * 0xfff) << 0).toString(16)}`;

  return (
    <>
      <div
        style={{
          margin: "0 auto",
          background: color,
          height: "100px",
          width: state.width,
        }}
      ></div>
      <button onClick={() => dispath("plus")}>
          .
      </button>
      <button onClick={() => dispath("minus")}>
          .
      </button>
    </>
  );
};


Sandkasten:





Aufgeschobene ("faule") Zustandsinitialisierung


useReducer verwendet ein drittes optionales Argument, eine Funktion, die ein Statusobjekt zurückgibt. Diese Funktion wird mit initialState als zweitem Argument aufgerufen.



Der Code:



const initializeState = () => ({
  width: 90,
});

//  ,  initializeState   
const initialState = { width: 0 };

const reducer = (state, action) => {
  switch (action) {
    case "plus":
      return { width: Math.min(state.width + 30, 600) };
    case "minus":
      return { width: Math.max(state.width - 30, 30) };
    default:
      throw new Error(" ?");
  }
};

const LazyState = () => {
  const [state, dispath] = useReducer(reducer, initialState, initializeState);
  const color = `#${((Math.random() * 0xfff) << 0).toString(16)}`;

  return (
    <>
      <div
        style={{
          margin: "0 auto",
          background: color,
          height: "100px",
          width: state.width,
        }}
      ></div>
      <button onClick={() => dispath("plus")}>
          .
      </button>
      <button onClick={() => dispath("minus")}>
          .
      </button>
    </>
  );
};


Sandkasten:





Simulation des Verhaltens von this.setState


useReducer verwendet einen weniger strengen Reduzierer als Redux. Zum Beispiel benötigt das zweite Argument, das an einen Reduzierer übergeben wird, nicht die type-Eigenschaft. Dies bietet uns interessante Möglichkeiten.



Der Code:



const initialState = { width: 30 };

const reducer = (state, newState) => ({
  ...state,
  width: newState.width,
});

const NewState = () => {
  const [state, setState] = useReducer(reducer, initialState);
  const color = `#${((Math.random() * 0xfff) << 0).toString(16)}`;

  return (
    <>
      <div
        style={{
          margin: "0 auto",
          background: color,
          height: "100px",
          width: state.width,
        }}
      ></div>
      <button onClick={() => setState({ width: 300 })}>
          .
      </button>
      <button onClick={() => setState({ width: 30 })}>
          .
      </button>
    </>
  );
};


Sandkasten:





useCallback



useCallback gibt den gespeicherten (zwischengespeicherten) Rückruf zurück.



Starter-Vorlage


Der Code:



const CallbackTemplate = () => {
  const [age, setAge] = useState(19);
  const handleClick = () => setAge(age < 100 ? age + 1 : age);
  const someValue = "some value";
  const doSomething = () => someValue;

  return (
    <>
      <Age age={age} handleClick={handleClick} />
      <Guide doSomething={doSomething} />
    </>
  );
};

const Age = ({ age, handleClick }) => {
  return (
    <div>
      <p> {age} .</p>
      <p>   </p>
      <button onClick={handleClick}> !</button>
    </div>
  );
};

const Guide = React.memo((props) => {
  const color = `#${((Math.random() * 0xfff) << 0).toString(16)}`;
  return (
    <div style={{ background: color, padding: ".4rem" }}>
      <p style={{ color: color, filter: "invert()" }}>
           .
      </p>
    </div>
  );
});


Sandkasten:







Im obigen Beispiel wird die Alterskomponente aktualisiert und neu gerendert, wenn auf die Schaltfläche geklickt wird. Die Guide-Komponente wird ebenfalls neu gerendert, wenn ein neuer Rückruf an die doSomething-Requisiten übergeben wird. Obwohl Guide React.memo verwendet, um die Leistung zu optimieren, wird es dennoch neu gezeichnet. Wie können wir das beheben?



Grundlegende Verwendung


Der Code:



const BasicCallback = () => {
  const [age, setAge] = useState(19);
  const handleClick = () => setAge(age < 100 ? age + 1 : age);
  const someValue = "some value";
  const doSomething = useCallback(() => someValue, [someValue]);

  return (
    <>
      <Age age={age} handleClick={handleClick} />
      <Guide doSomething={doSomething} />
    </>
  );
};

const Age = ({ age, handleClick }) => {
  return (
    <div>
      <p> {age} .</p>
      <p>   </p>
      <button onClick={handleClick}> !</button>
    </div>
  );
};

const Guide = React.memo((props) => {
  const color = `#${((Math.random() * 0xfff) << 0).toString(16)}`;
  return (
    <div style={{ background: color, padding: ".4rem" }}>
      <p style={{ color: color, filter: "invert()" }}>
           .
      </p>
    </div>
  );
});


Sandkasten:







Eingebauter useCallback


useCallback kann als integrierte Funktion verwendet werden.



Der Code:



const InlineCallback = () => {
  const [age, setAge] = useState(19);
  const handleClick = () => setAge(age < 100 ? age + 1 : age);
  const someValue = "some value";

  return (
    <>
      <Age age={age} handleClick={handleClick} />
      <Guide doSomething={useCallback(() => someValue, [someValue])} />
    </>
  );
};

const Age = ({ age, handleClick }) => {
  return (
    <div>
      <p> {age} .</p>
      <p>   </p>
      <button onClick={handleClick}> !</button>
    </div>
  );
};

const Guide = React.memo((props) => {
  const color = `#${((Math.random() * 0xfff) << 0).toString(16)}`;
  return (
    <div style={{ background: color, padding: ".4rem" }}>
      <p style={{ color: color, filter: "invert()" }}>
           .
      </p>
    </div>
  );
});


Sandkasten:





useMemo



useMemo gibt den gespeicherten (zwischengespeicherten) Wert zurück.



Starter-Vorlage


Der Code:



const MemoTemplate = () => {
  const [age, setAge] = useState(19);
  const handleClick = () => setAge(age < 100 ? age + 1 : age);
  const someValue = { value: "some value" };
  const doSomething = () => someValue;

  return (
    <>
      <Age age={age} handleClick={handleClick} />
      <Guide doSomething={doSomething} />
    </>
  );
};

const Age = ({ age, handleClick }) => {
  return (
    <div>
      <p> {age} .</p>
      <p>   </p>
      <button onClick={handleClick}> !</button>
    </div>
  );
};

const Guide = React.memo((props) => {
  const color = `#${((Math.random() * 0xfff) << 0).toString(16)}`;
  return (
    <div style={{ background: color, padding: ".4rem" }}>
      <p style={{ color: color, filter: "invert()" }}>
           .
      </p>
    </div>
  );
});


Sandkasten:





Diese Vorlage ist identisch mit der Startvorlage useCallback, außer dass someValue ein Objekt und keine Zeichenfolge ist. Die Guide-Komponente wird trotz Verwendung von React.memo ebenfalls neu gerendert.



Aber warum passiert das? Schließlich werden Objekte anhand von Referenzen verglichen, und die Referenz auf someValue ändert sich mit jedem Rendering. Irgendwelche Ideen?



Grundlegende Verwendung


Der von doSomething zurückgegebene Wert kann mit useMemo gespeichert werden. Dies verhindert unnötiges Rendern.



Der Code:



const BasicMemo = () => {
  const [age, setAge] = useState(19);
  const handleClick = () => setAge(age < 100 ? age + 1 : age);
  const someValue = () => ({ value: "some value" });
  const doSomething = useMemo(() => someValue, []);

  return (
    <>
      <Age age={age} handleClick={handleClick} />
      <Guide doSomething={doSomething} />
    </>
  );
};

const Age = ({ age, handleClick }) => {
  return (
    <div>
      <p> {age} .</p>
      <p>   </p>
      <button onClick={handleClick}> !</button>
    </div>
  );
};

const Guide = React.memo((props) => {
  const color = `#${((Math.random() * 0xfff) << 0).toString(16)}`;
  return (
    <div style={{ background: color, padding: ".4rem" }}>
      <p style={{ color: color, filter: "invert()" }}>
           .
      </p>
    </div>
  );
});


Sandkasten:





useRef


useRef gibt ein ref-Objekt zurück. Die Werte dieses Objekts sind über die Eigenschaft "current" verfügbar. Dieser Eigenschaft kann ein Anfangswert zugewiesen werden: useRef (initialValue). Ein Referenzobjekt existiert für die Lebensdauer einer Komponente.



Zugriff auf das DOM


Der Code:



const DomAccess = () => {
  const textareaEl = useRef(null);
  const handleClick = () => {
    textareaEl.current.value =
      " - ,     . ,    !";
    textareaEl.current.focus();
  };
  const color = `#${((Math.random() * 0xfff) << 0).toString(16)}`;

  return (
    <>
      <button onClick={handleClick}>
         .
      </button>
      <label htmlFor="msg">
                 .
      </label>
      <textarea ref={textareaEl} id="msg" />
    </>
  );
};


Sandkasten:





Instanzähnliche Variablen (Generika)


Ein ref-Objekt kann einen beliebigen Wert enthalten, nicht nur einen Zeiger auf ein DOM-Element.



Der Code:



const StringVal = () => {
  const textareaEl = useRef(null);
  const stringVal = useRef(
    " - ,     . ,    !"
  );
  const handleClick = () => {
    textareaEl.current.value = stringVal.current;
    textareaEl.current.focus();
  };
  const color = `#${((Math.random() * 0xfff) << 0).toString(16)}`;

  return (
    <>
      <button onClick={handleClick}>
         .
      </button>
      <label htmlFor="msg">
               .
      </label>
      <textarea ref={textareaEl} id="msg" />
    </>
  );
};


Sandkasten:





Mit useRef kann die ID des Timers gespeichert werden, damit sie später gestoppt werden kann.



Der Code:



const IntervalRef = () => {
  const [time, setTime] = useState(0);
  const setIntervalRef = useRef();

  useEffect(() => {
    const id = setInterval(() => {
      setTime((time) => (time = new Date().toLocaleTimeString()));
    }, 1000);

    setIntervalRef.current = id;

    return () => clearInterval(setIntervalRef.current);
  }, [time]);

  return (
    <>
      <p> :</p>
      <time>{time}</time>
    </>
  );
};


Sandkasten:





Ich hoffe dir hat der Artikel gefallen. Danke für die Aufmerksamkeit.



All Articles