Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Standard way to fetch from URL #806

Open
moshthepitt opened this issue Apr 17, 2020 · 10 comments
Open

Standard way to fetch from URL #806

moshthepitt opened this issue Apr 17, 2020 · 10 comments

Comments

@moshthepitt
Copy link
Contributor

moshthepitt commented Apr 17, 2020

I have noticed that we have different ways, and probably differing views on how to deal with components that fetch data from some URL, and then display it.

In dealing with such components, we need to consider:

  1. That we need to show some indication that we are loading data. But we should only show this when the fetch is underway
  2. That we can get an error from the fetch call
  3. That it is possible for the fetch call to return something false-y despite not error-ing out e.g. an empty array. We need to deal with this possibility through something other than showing a loading indicator
  4. That we can, and expect to, get a valid response from the fetch, after which we should render what we actually wanted to render

I think that one way of dealing with this is to structure the component like this:

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

export const NiceComponent = () => {
  // we start out in a "loading" state
  const [loading, setLoading] = useState<boolean>(true);

  async function loadData() {
    try {
      // do your fetch here to get the data
      await fetch('https://exmaple.com/foobar').then(response => {
        if (!response) {
          // this means we got something false-ey from the fetch and we need
          // to deal with it
        }

        // do something with the valid response
      });
    } catch (error) {
      // do something with the error
      console.log(error);
    } finally {
      // set loading to false because in one way or another we have fetched
      // from the URL and either gotten the data or an error
      // in any case, we are not "loading" any more
      setLoading(false);
    }
  }

  useEffect(() => {
    loadData().catch(error => {
      // do something with the error
      console.log(error);
    });
  }, []);

  if (loading === true) {
    return <p>this is the loading message/indicator</p>;
  }

  // finally we render whatever we want
  return <p>something</p>;
};

Does anyone have a different opinion, or is there something that I have missed? Let us discuss and come up with a standard way of doing it.

@moshthepitt
Copy link
Contributor Author

@p-netm @RayceeM for some reason Github will not let me assign you here.

@peterMuriuki
Copy link
Collaborator

peterMuriuki commented Apr 17, 2020

yeah, it makes sense that we should standardize this. I like it. The only concerns i might raise are:

For some of the components we have factored out the data loading functions to utils files, which I think is nice(for Reveal-fe). I am not sure how the pattern for using setLoading in these functions would look like. I guess the easiest would be to pass an optional stateSetter function to the data-loading function. (PS: consider EdgeCases where component makes use of 2 or more of such data-loading functions!!)

    } finally {
      // set loading to false because in one way or another we have fetched
      // from the URL and either gotten the data or an error
      // in any case, we are not "loading" any more
      setLoading(false);
    }

I know this is more of a draft, but we need to consider that this might result in the setState on unmounted component error we had a while back, this might be trivial to fix.

@moshthepitt
Copy link
Contributor Author

I know this is more of a draft, but we need to consider that this might result in the setState on unmounted component error we had a while back, this might be trivial to fix.

@p-netm How would you fix it in the example component above?

@peterMuriuki
Copy link
Collaborator

I also believe we would need to do something like setLoading(props.data) and then in the final block setLoading(false) if loading

@moshthepitt
Copy link
Contributor Author

I also believe we would need to do something like setLoading(props.data) and then in the final block setLoading(false) if loading

@p-netm maybe it would be best to write up a quick demo component that does this in a comment here?

@peterMuriuki
Copy link
Collaborator

ok.

@peterMuriuki
Copy link
Collaborator

I know this is more of a draft, but we need to consider that this might result in the setState on unmounted component error we had a while back, this might be trivial to fix.

@p-netm How would you fix it in the example component above?

import React, { useEffect, useRef, useState } from 'react';

export const NiceComponent = () => {
  // we start out in a "loading" state
  const [loading, setLoading] = useState<boolean>(true);
  const isMounted = useRef<boolean>(false);

  async function loadData() {
    try {
      // do your fetch here to get the data
      await fetch('https://exmaple.com/foobar').then(response => {
        if (!response) {
          // this means we got something false-ey from the fetch and we need
          // to deal with it
        }

        // do something with the valid response
      });
    } catch (error) {
      // do something with the error
      console.log(error);
    } finally {
      // set loading to false because in one way or another we have fetched
      // from the URL and either gotten the data or an error
      // in any case, we are not "loading" any more
      if (isMounted.current) {
        setLoading(false);
      }
    }
  }

  useEffect(() => {
    isMounted.current = true;
    loadData().catch(error => {
      // do something with the error
      console.log(error);
    });
    return () => {
      isMounted.current = false;
    };
  }, []);

  if (loading === true) {
    return <p>this is the loading message/indicator</p>;
  }

  // finally we render whatever we want
  return <p>something</p>;
};

@peterMuriuki
Copy link
Collaborator

@moshthepitt
What are your thoughts on https://www.npmjs.com/package/react-async

@moshthepitt
Copy link
Contributor Author

@p-netm sounds fantastic. We should try and use it and see.

@cKellyDesign
Copy link
Contributor

What are your thoughts on https://www.npmjs.com/package/react-async

Good find @p-netm, this looks cool and seems like it can offer us a lot!

  • useFetch could be used in our existing services, not sure the best way to surface the state properties to the component calling the services though

  • useAsync feels like it would be appropriate for cases where we need multiple/sequential API calls, would/could a useAsync promiseFn method be a collection* of useFetchs?

  • the state properties looks like it gives us access to everything we could dream of!

  • collection - this could be a simple list of calls to make (handled by returning Promise.all(...) in the promiseFn) and/or a function containing business logics (and is responsible for making services calls)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment