Welcome toVigges Developer Community-Open, Learning,Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
3.1k views
in Technique[技术] by (71.8m points)

javascript - React custom hook: can't get an async function

I've got a button that calls an async function, that is returned by a call to a custom React hook, alongside with a reactive prop that I need to keep track of.

CodeSandbox here.

// useEmail.js
import { useState } from "react";

export default function useEmail(message) {
  const [returnedMessage, setReturnedMessage] = useState("old");

  const send = async () => {
    // fake fetch
    const whatever = await fetch(
      "https://jsonplaceholder.typicode.com/todos/1"
    );
    setReturnedMessage("new");
  };

  return {
    returnedMessage,
    send
  };
}

And this is the app

// app.js
import React from "react";
import useEmail from "./useEmail";

export default function App() {
  const { returnedMessage, send } = useEmail();
  const run = async () => {
    console.log("returnMessage PRE", returnedMessage);
    await send();
    console.log("returnMessage POST", returnedMessage);
  };
  return (
    <div className="App">
      <h2>Click and wait for 1 second</h2>
      <button onClick={run}>Click me</button>
      <h2>Returned message:</h2>
      <p>{returnedMessage}</p>
      <button onClick={() => window.location.reload()}>
        Reload to test again
      </button>
      <p>
        It prints "new", but logs "old"
        <br />
        even if I await send()...?
      </p>
    </div>
  );
}

useEmail returns both a returnMessage string, that is initialized as "old", and an async function send that fetches something, then flips the returnMessage and sets it to "new".

How is it possible that in the <p>{returnedMessage}</p> the value correctly turns from "old" to "new", while the Console logs always "old", even if I await when calling send()?

enter image description here

It seems like send() is not really treated as an asynchronous function – I've tried in different ways but I always have a correctly updated rendering but a wrong value when I need it in the function for further processing.

Thank you for your help


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

You can do the job using useRef.

It seems you can't access the updated value without running the hook again. With useRef you'll get a reference and you can access the data at any time, without running the hook again.

// useEmail.js

export default function useEmail(message) {
  const messageRef = React.useRef("old");

  const send = async () => {
    // fake fetch
    const whatever = await fetch(
      "https://jsonplaceholder.typicode.com/todos/1"
    );

    messageRef.current = "new";
  };

  return {
    messageRef,
    send
  };
}
// app.js

export default function App() {
  const { messageRef, send } = useEmail();
  const run = async () => {
    console.log("returnMessage PRE", messageRef.current);
    await send();
    console.log("returnMessage POST", messageRef.current);
  };

  return (
    <div className="App">
      <h2>Click and wait for 1 second</h2>
      <button onClick={run}>Click me</button>
      <h2>Returned message:</h2>
      <p>{returnedMessage}</p>
      <button onClick={() => window.location.reload()}>
        Reload to test again
      </button>
      <p>
        It prints "new", but logs "old"
        <br />
        even if I await send()...?
      </p>
    </div>
  );
}

enter image description here


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to Vigges Developer Community for programmer and developer-Open, Learning and Share
...