9

I tried to create a function for fetching data from the server, and it works. But I am not sure if that the right way?

I created a function component to fetching data, using useState, useEffect and Async/Await :

import React, { useState, useEffect } from "react";

const Fetch = () => {
  const [data, setData] = useState(null);
  useEffect(() => {
    const fetchData = async () => {
      let res = await fetch(
        "https://api.coindesk.com/v1/bpi/currentprice.json" //example and simple data
      );
      let response = await res.json();
      setData(response.disclaimer); // parse json
      console.log(response);
    };
    fetchData();
  }, []);
  return <div>{data}</div>;
};

export default Fetch; // don't run code snippet, not working, this component must be imported in main

Where I am not sure is a place where to call the fetchData function. I do that inside useEffect? Right place? And, this call will happen only one? Because i use []?

Generally, how would you do something like this?

Milos N.
  • 4,416
  • 6
  • 18
  • 31
  • 1
    Your code looks fine. Yes, you do it in useEffect. Yes, it happens just once because you used `[ ]`. – Nicholas Tower Mar 05 '19 at 17:09
  • Ok, right place is below function to call her? – Milos N. Mar 05 '19 at 17:19
  • Yes the call will happen only on component mounting because of the empty array. The array in that second argument give the hook what to watch for changes and only too apply the effect if any of those dependencies has changed. Passing in an empty tells the hook that your component does not depend on props/state and thus will only apply the hook on component mount. – Thulani Chivandikwa Jun 04 '19 at 21:31
  • Possible duplicate of [React Hook Warnings for async function in useEffect: useEffect function must return a cleanup function or nothing](https://stackoverflow.com/questions/53332321/react-hook-warnings-for-async-function-in-useeffect-useeffect-function-must-ret) – Aleksi Jun 13 '19 at 08:02

2 Answers2

11

Overall, you are heading in the right direction. For fetching data, you'd wanna use useEffect and pass [] as a second argument to make sure it fires only on initial mount.

I believe you could benefit from decoupling fetchJson function and making it more generic, as such:

const fetchJson = async (url) => {
  const response = await fetch(url);
  return response.json();
};

const Fetch = () => {
  const [data, setData] = useState(null);

  useEffect(() => {
    fetchJson("https://api.coindesk.com/v1/bpi/currentprice.json")
      .then(({ disclaimer }) => setData(disclaimer));
  }, []);

  return <div>{data}</div>;
};
Pa Ye
  • 1,779
  • 12
  • 22
  • 1
    In this example you would need to await the fetchData call or do a .then. Unless I am missing something? – Thulani Chivandikwa Jun 04 '19 at 21:24
  • 1
    Also, I don't see how this would work as you can't use `await` outside of an `async` function, can you? So the `await fetchData` is invalid. – adam-beck Jul 06 '19 at 12:12
  • 1
    I am new to hooks, but wouldn't you need to use setData(disclaimer) instead of setState(disclaimer)? – Syl OR Oct 04 '19 at 15:50
7

Another option is to use a self invoking function:

const Fetch = () => {
  const [data, setData] = useState(null);
  useEffect(() => {
    (async () => {
      let res = await fetch(
        "https://api.coindesk.com/v1/bpi/currentprice.json" //example and simple data
      );
      let response = await res.json();
      setData(response);
    })();
  }, []);
  return <div>{data}</div>;
};

The suggestion to separate out the fetch logic to a separate function is a good idea and can be done as follows:

const Fetch = () => {
  const [data, setData] = useState(null);
  useEffect(() => {
    (async () => {
      let response= await fetchData("https://api.coindesk.com/v1/bpi/currentprice.json");
      setData(response);
    })();
  }, []);
  return <div>{data}</div>;
};

const fetchData = async (url) => {
  const response = await fetch(url);
  const json = await response.json();

  return json;
};

And yet another option is to create a wrapper function around useEffect that triggers the async function for you similar to this:

export function useAsyncEffect(effect: () => Promise<any>) {
  useEffect(() => {
    effect().catch(e => console.warn("useAsyncEffect error", e));
  });
}
Thulani Chivandikwa
  • 3,402
  • 30
  • 33