简体   繁体   中英

Reuse data with react hooks

I need to reuse data in my app with hook, so I created a hook called useData

import { useState } from "react";

export default function useData() {
  const [message, setMessage] = useState("");
  const [type, setType] = useState("error");

  return [{ message, type }, { setMessage, setType }];
}

And then I use my hook in my app like below.

function App() {
  const [data, action] = useData();

  const handleClick = () => {
    action.setMessage("Hello message");
    action.setType("info");

    alert(JSON.stringify(data));
  };

  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>
      <button className="btn" onClick={handleClick}>
        Alert Data
      </button>
    </div>
  );
}

When I clicked the button Alert Data at the first time it will alert

{message: "", type: "error"}

My expectation is

{message: "Hello message", type: "info"}

What am I doing it wrong here? Please let me know, and is there any way to fix this code?

Here's the codesandbox code: https://codesandbox.io/embed/romantic-hugle-zm2e2?fontsize=14&hidenavigation=1&theme=dark

Thanks for your attention.

You're counting on data being updated the instant you call your onClick handler, but that won't be true until the next render. Instead, consider using a useEffect hook. In the following example, I've also created a stateful open variable to tell the modal when to be open.

function App() {
  const [data, action] = useData();
  const [open, setOpen] = useState(false);

  const handleClick = () => {
    action.setMessage("Hello message");
    action.setType("info");
    setOpen(true);
  };

  useEffect(() => {
    if (open) {
      alert(JSON.stringify(data));
      setOpen(false);
    }
  }, [open, data])

  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>
      <button className="btn" onClick={handleClick}>
        Alert Data
      </button>
    </div>
  );
}

In React.js, setState isn't a synchronous function. That is, setState doesn't change the state instantly. For this case, setState has a version with the completion callback. You can define a custom Hook with the completion callback. Fortunately, there is an npm package called "use-state-with-callback".

Or you can write the code as follows.

useData.js

 export default function useData(msg, cb) { const [message, setMessage] = useState(msg); useEffect(() => cb(message), [message]); return [message, setMessage]; }

index.js

 function App() { const callback = (msg) => { alert(JSON.stringify(msg)); } const [message, setMessage] = useData({ content: "", type: "error" }, callback); const handleClick = () => { setMessage({ content: "Hello message", type: "info" }); }; return ( <div className="App"> <h1>Hello CodeSandbox</h1> <h2>Start editing to see some magic happen!</h2> <button className="btn" onClick={handleClick}> Alert Data </button> </div> ); } const rootElement = document.getElementById("root"); ReactDOM.render(<App />, rootElement);

This works fine.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM