So verwenden Sie Callback mit useState-Hook in React [duplicate]

Lesezeit: 10 Minuten

Benutzeravatar von Atul
Atul

Ich verwende Funktionskomponenten mit Haken. Ich muss den Status in Eltern von einem Kind aktualisieren. Ich verwende eine Prop-Funktion in Parent. Alles funktioniert gut, außer dass meine Prop-Funktion den vorherigen Zustand und nicht den aktuellen Zustand erhält. Meine Prop-Funktion wird vorher ausgeführt useState Hook-Einstellung aktuellen Zustand. Wie kann ich warten, bis meine Rückruffunktion nach dem useState-Aufruf ausgeführt wird? Ich suche sowas wie setState(Zustand, Rückruf) aus klassenbasierten Komponenten.

Hier ist das Code-Snippet:

function Parent() {
  const [Name, setName] = useState("");
  getChildChange = getChildChange.bind(this);
  function getChildChange(value) {
    setName(value);
  }

  return <div> {Name} :
    <Child getChildChange={getChildChange} ></Child>
  </div>
}

function Child(props) {
  const [Name, setName] = useState("");
  handleChange = handleChange.bind(this);

  function handleChange(ele) {
    setName(ele.target.value);
    props.getChildChange(collectState());
  }

  function collectState() {
    return Name;
  }

  return (<div>
    <input onChange={handleChange} value={Name}></input>
  </div>);
} 

  • warum übergibst du dich nicht einfach? setName und nennen Sie es vom Kind?

    – dan-classon

    2. März 2019 um 3:41 Uhr

  • Ich hoffe wir bekommen interessante Kommentare in diesem Thread github.com/facebook/react/issues/17969

    – RajaSekhar K

    19. April 2020 um 3:40 Uhr

  • Es gibt eine einfache Möglichkeit, dies ohne useEffect zu tun: stackoverflow.com/a/70405577/5823517

    – Tunn

    18. Dezember 2021 um 17:37 Uhr


  • Die meisten Antworten legen zu viel Wert darauf, dass sich dies wie eine klassenbasierte Komponente verhält. Das ist ein roter Hering. Das eigentliche Problem ist, dass das Kind anruft getChildChange mit dem alten Wert. Ändern Sie es zu props.getChildChange(ele.target.value) würde es lösen. Siehe stackoverflow.com/a/56267744/367796

    – Steinbot

    26. Juni um 20:47 Uhr

Benutzeravatar von Robin Wieruch
Robin Wieruch

Sie können useEffect/useLayoutEffect verwenden, um dies zu erreichen:

const SomeComponent = () => {
  const [count, setCount] = React.useState(0)

  React.useEffect(() => {
    if (count > 1) {
      document.title="Threshold of over 1 reached.";
    } else {
      document.title="No threshold reached.";
    }
  }, [count]);

  return (
    <div>
      <p>{count}</p>

      <button type="button" onClick={() => setCount(count + 1)}>
        Increase
      </button>
    </div>
  );
};

Wenn Sie verhindern möchten, dass der Rückruf weiterläuft zuerst rendernvorherige Version anpassen:

const SomeComponent = () => {
  const [count, setCount] = React.useState(0)

  const didMount = React.useRef(false);

  React.useEffect(() => {
    if (!didMount.current) {
      didMount.current = true;
      return;
    }

    if (count > 1) {
      document.title="Threshold of over 1 reached.";
    } else {
      document.title="No threshold reached.";
    }
  }, [count]);

  return (
    <div>
      <p>{count}</p>

      <button type="button" onClick={() => setCount(count + 1)}>
        Increase
      </button>
    </div>
  );
};

Mehr dazu weiter unten hier.

  • Ich versuche, use-state-with-callback zu installieren, aber es funktioniert nicht. Es gibt einen Fehler. Was kann ich machen?

    – Efe FRK

    29. April 2021 um 17:13 Uhr


Benutzeravatar von ford04
ford04

setState(updater, callback) zum useState

Die folgende Umsetzung kommt dem Original sehr nahe setState Rückruf von Klassen.

Verbesserungen an der akzeptierten Antwort:

  1. Die Callback-Ausführung wird beim anfänglichen Rendern weggelassen – wir wollen sie nur im Zustand aufrufen Aktualisierung
  2. Der Rückruf kann jeweils dynamisch sein setState Aufruf, wie bei Klassen

Verwendungszweck

const App = () => {
  const [state, setState] = useStateCallback(0); // same API as useState

  const handleClick = () => {
    setState(
      prev => prev + 1,
      // second argument is callback, `s` being the *updated* state
      s => console.log("I am called after setState, state:", s)
    );
  };

  return <button onClick={handleClick}>Increment</button>;
}

useStateCallback

function useStateCallback(initialState) {
  const [state, setState] = useState(initialState);
  const cbRef = useRef(null); // init mutable ref container for callbacks

  const setStateCallback = useCallback((state, cb) => {
    cbRef.current = cb; // store current, passed callback in ref
    setState(state);
  }, []); // keep object reference stable, exactly like `useState`

  useEffect(() => {
    // cb.current is `null` on initial render, 
    // so we only invoke callback on state *updates*
    if (cbRef.current) {
      cbRef.current(state);
      cbRef.current = null; // reset callback after execution
    }
  }, [state]);

  return [state, setStateCallback];
}

TypeScript-Version

function useStateCallback<T>(
  initialState: T
): [T, (state: T, cb?: (state: T) => void) => void] {
  const [state, setState] = useState(initialState);
  const cbRef = useRef<((state: T) => void) | undefined>(undefined); // init mutable ref container for callbacks

  const setStateCallback = useCallback((state: T, cb?: (state: T) => void) => {
    cbRef.current = cb; // store current, passed callback in ref
    setState(state);
  }, []); // keep object reference stable, exactly like `useState`

  useEffect(() => {
    // cb.current is `undefined` on initial render,
    // so we only invoke callback on state *updates*
    if (cbRef.current) {
      cbRef.current(state);
      cbRef.current = undefined; // reset callback after execution
    }
  }, [state]);

  return [state, setStateCallback];
}

Weitere Infos: React Hooks FAQ: Gibt es so etwas wie Instanzvariablen?

Arbeitsbeispiel

const App = () => {
  const [state, setState] = useStateCallback(0);

  const handleClick = () =>
    setState(
      prev => prev + 1,
      // important: use `s`, not the stale/old closure value `state`
      s => console.log("I am called after setState, state:", s)
    );

  return (
    <div>
      <p>Hello Comp. State: {state} </p>
      <button onClick={handleClick}>Click me</button>
    </div>
  );
}

function useStateCallback(initialState) {
  const [state, setState] = useState(initialState);
  const cbRef = useRef(null);

  const setStateCallback = useCallback((state, cb) => {
    cbRef.current = cb; 
    setState(state);
  }, []);

  useEffect(() => {
    if (cbRef.current) {
      cbRef.current(state);
      cbRef.current = null;
    }
  }, [state]);

  return [state, setStateCallback];
}

ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.0/umd/react.production.min.js" integrity="sha256-32Gmw5rBDXyMjg/73FgpukoTZdMrxuYW7tj8adbN8z4=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.0/umd/react-dom.production.min.js" integrity="sha256-bjQ42ac3EN0GqK40pC9gGi/YixvKyZ24qMP/9HiGW7w=" crossorigin="anonymous"></script>
<script>var { useReducer, useEffect, useState, useRef, useCallback } = React</script>
<div id="root"></div>

  • was macht cbRef.current(state); do in diesem Code innerhalb der Bedingung in der useEffect?

    – bot19

    17. Juni 2021 um 1:50 Uhr

  • @bot19 das ist der eigentliche Aufruf des Callbacks, der vorher per gesetzt wurde setState(..,cb). cbRef.current speichert eine Funktion. Diese Funktion heißt dann – (state) – mit dem aktuellen, aktualisierten Stand.

    – ford04

    17. Juni 2021 um 4:26 Uhr

  • @dwjohnston Ausstieg aus Zustandsaktualisierungen bei identischen Werten ist die neue React-Voreinstellung für Hooks – daher würde ich dieses Verhalten in den meisten Fällen nicht ändern. Wenn Sie aus Legacy-Gründen wirklich inline mit dem alten klassenbasierten Vergleich sein müssen (der sich aufgrund der Objektzusammenführung so verhält), sieht der Codesandbox-Ansatz vernünftig aus! Anstatt zu verwenden Symbolkönnen Sie den Statuswert auch jedes Mal in einen neuen Objektcontainer einschließen.

    – ford04

    8. September 2021 um 12:00 Uhr

  • codesandbox.io/s/gifted-dhawan-hedsp?file=/src/App.tsx – Ich habe das Array verwendet und dem Callback die aktuelle Statusoption hinzugefügt. Es gibt auch ein Beispiel mit dem Doppel-Update. Hoffe es hilft jemandem 🙂

    – Petr Újezdský

    13. September 2021 um 18:43 Uhr

  • @PetrÚjezdský Danke für deine Ideen! Zu 1: Ich denke, dieser Kommentar passt gut. Zu 2: Wenn Sie anrufen setState zweimal während gleich Renderzyklus und dieselbe Hook-Instanz gewinnt der letzte Wert in React. Also würde ich beim Setzen eines Callbacks gleiches Verhalten erwarten und wäre eher verwirrt, wenn beide alt sind und neue Rückrufe werden aufgerufen. Beides scheint sowieso eher ein Grenzfall zu sein – höchstwahrscheinlich werden Sie einen Ereignishandler haben, bei dem das Festlegen des Status in verschiedenen Renderings erfolgt

    – ford04

    18. September 2021 um 11:27 Uhr


Benutzeravatar von Abhijith Sasikumar
Abhijith Sasikumar

Mit React16.x und höher, wenn Sie eine Callback-Funktion bei Zustandsänderung mit aufrufen möchten useState Haken, können Sie die verwenden useEffect Haken, der an die Zustandsänderung angehängt ist.

import React, { useEffect } from "react";

useEffect(() => {
  props.getChildChange(name); // using camelCase for functions is recommended.
}, [name]); // this will call getChildChange on initial render and when ever name changes.

  • Was sollen wir tun, wenn es mehr als eine Funktion gibt und nur eine davon in der Wiedergabe funktionieren muss?

    – Gukal

    19. Januar 2021 um 10:29 Uhr

  • @Gucal Sie können useEffect mehrmals verwenden useEffect(() => loadFunctionAOnce()). useEffect(() => loadFunctionBIfNameChange(), [name])

    – DAMIEN JIANG

    3. Mai 2021 um 21:21 Uhr


  • Dadurch wird auch props.getChildChange beim ersten Rendern ausgeführt

    – 0xAnon

    15. September 2021 um 7:45 Uhr

  • Einzige Lösung, die kurz und einfach ist

    – Antonius

    30. Juni um 12:33 Uhr

Benutzeravatar von 徐銘谷
徐銘谷

Eigentlich sollten Sie die Verwendung vermeiden this bei Verwendung von Reaktionshaken. Es verursacht Nebenwirkungen. Deshalb React Team Create react hooks.

Wenn Sie Codes entfernen, die zu binden versuchen thiskönnen Sie einfach weitergeben setName von Parent zu Child und rufe es an handleChange. Sauberer Code!

function Parent() {
  const [Name, setName] = useState("");

  return <div> {Name} :
    <Child setName={setName} ></Child>
  </div>
}

function Child(props) {
  const [Name, setName] = useState("");

  function handleChange(ele) {
    setName(ele.target.value);
    props.setName(ele.target.value);
  }

  return (<div>
    <input onChange={handleChange} value={Name}></input>
  </div>);
} 

Außerdem müssen Sie nicht zwei Kopien von erstellen Name(eins drin Parent und der andere drin Child). Halten Sie sich an das „Single Source of Truth“-Prinzip, Child muss den Staat nicht besitzen Name aber erhalte es von Parent. Cleaner-Knoten!

function Parent() {
  const [Name, setName] = useState("");

  return <div> {Name} :
    <Child setName={setName} Name={Name}></Child>
  </div>
}

function Child(props) {    
  function handleChange(ele) {
    props.setName(ele.target.value);
  }

  return (<div>
    <input onChange={handleChange} value={props.Name}></input>
  </div>);
} 

Wir können eine benutzerdefinierte Funktion schreiben, die die CallBack-Funktion aufruft, wenn sich der Status ändert

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

import "./styles.css";

const useStateCallbackWrapper = (initilValue, callBack) => {
  const [state, setState] = useState(initilValue);
  useEffect(() => callBack(state), [state]);
  return [state, setState];
};

const callBack = state => {
  console.log("---------------", state);
};
function App() {
  const [count, setCount] = useStateCallbackWrapper(0, callBack);
  return (
    <div className="App">
      <h1>{count}</h1>
      <button onClick={() => setCount(count + 1)}>+</button>
      <h2>Start editing to see some magic happen!</h2>
    </div>
  );
}

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

`

  • Diese Lösung scheitert beim Produktionsaufbau mit React Hook useEffect has a missing dependency: 'callBack'. Either include it or remove the dependency array. If 'callBack' changes too often, find the parent component that defines it and wrap that definition in useCallback react-hooks/exhaustive-deps

    – Pablo Albaladejo

    7. Februar 2020 um 16:30 Uhr

  • versuchen Sie, die useEffect-Zeile wie beizubehalten useEffect(() => callBack?callBack(state):null, [state, callBack]);

    – RajaSekhar K

    18. April 2020 um 19:11 Uhr

Benutzeravatar von james h
Jakob H

Eine andere Möglichkeit, dies zu erreichen:

const [Name, setName] = useState({val:"", callback: null});
React.useEffect(()=>{
  console.log(Name)
  const {callback} = Name;
  callback && callback();
}, [Name]);
setName({val:'foo', callback: ()=>setName({val: 'then bar'})})

  • Diese Lösung scheitert beim Produktionsaufbau mit React Hook useEffect has a missing dependency: 'callBack'. Either include it or remove the dependency array. If 'callBack' changes too often, find the parent component that defines it and wrap that definition in useCallback react-hooks/exhaustive-deps

    – Pablo Albaladejo

    7. Februar 2020 um 16:30 Uhr

  • versuchen Sie, die useEffect-Zeile wie beizubehalten useEffect(() => callBack?callBack(state):null, [state, callBack]);

    – RajaSekhar K

    18. April 2020 um 19:11 Uhr

Der Benutzer-Avatar von SpülmaschineWithProgrammingSkill
GeschirrspülerMitProgrammierfähigkeit

kannst du nutzen Rückruf verwenden Haken, um dies zu tun.

function Parent() {
  const [name, setName] = useState("");
  const getChildChange = useCallback( (updatedName) => {
    setName(updatedName);
  }, []);

  return <div> {name} :
    <Child getChildChange={getChildChange} ></Child>
  </div>
}

function Child(props) {
  const [name, setName] = useState("");

  function handleChange(ele) {
    setName(ele.target.value);
    props.getChildChange(ele.target.value);
  }

  function collectState() {
    return name;
  }

  return (<div>
    <input onChange={handleChange} value={name}></input>
  </div>);
}

  • Das Festlegen des Status in zwei Komponenten für dieselbe Variable klingt für mich nicht nach einer guten Idee.

    – Isaak Pak

    28. August 2020 um 20:44 Uhr

  • useState Hook doesn't support the second callback argument. To execute a side effect after rendering, declare it in the component body with useEffect(). Dies ist, was ich bekomme, nachdem ich einen Rückruf getätigt habe

    – Hidayt Rahman

    7. Juni 2021 um 5:36 Uhr

  • Hey @dishwasherWithProgrammingSkill, wofür wird dieser Code verwendet? Was unterscheidet sich von setState inline so: <Child getChildChange={(value) => setValue(value)} ></Child>

    – tmohammad78

    26. November 2021 um 19:28 Uhr


1436630cookie-checkSo verwenden Sie Callback mit useState-Hook in React [duplicate]

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy