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

Add documentation on how to best chain requests. #233

Closed
PupoSDC opened this issue May 26, 2020 · 3 comments
Closed

Add documentation on how to best chain requests. #233

PupoSDC opened this issue May 26, 2020 · 3 comments
Assignees

Comments

@PupoSDC
Copy link

PupoSDC commented May 26, 2020

Hello!

I have been looking everywhere and trying to have a go at this myself, but I'm still haven't found a pattern that allows me to chain two axios hooks sequentially. Using vanilla axios I could do something like this:

useEffect(async () => { // ignore the annoying console error for example sake
   const {data: value} = await axios.get('/api/1');
   const {data: value2} = await axios.get(`/api/${value}`);
   setState(value2);
});

however this kind of solution inside of a useEffect is kind of meh, since I loose all the goodies from axios-hooks. These are the functionalities from axios-hooks i want to keep leveraging:

  • not having to keep track of loading and error state myself
  • using the cache of useAxios, instead of adding yet another library to the project

The problems i'm facing are:

  • No ability to await getValueOne and call getValueTwo immediatly after
  • between the two requests there is a gap where nothing is loading which causes my application to flash my compoment before all data as loaded. i fixes this with another loading state as seen in the example

Here is my current implementation of a 2 sequential axios hooks calls:

export default (valueZero: string): ResponseValues<string> => {
  // Gaps the bridge between the first call finishing and the second starting
  const [loading, setLoading] = useState(false);

  const [{
    data: valueOne,
    error: errorOne,
    loading: loadingOne,
  }, getValueOne] = useAxios(`/api/${valueZero}`, {manual: true});

  const [{
    data: valueTwo = '',
    error: errorTwo,
    loading: loadingTwo,
  }, getValueTwo] = useAxios(`api/${valueOne}`, {manual: true});

  useEffect(function getFirstValue() {
    if (valueZero) {
      setLoading(true);
      getValueOne(undefined, {useCache: true});
    }
  }, [valueZero, getValueOne]);

  useEffect(function getSecondValue() {
    if (valueTwo) {
      setLoading(false);
      getValueTwo(undefined, {useCache: true});
    }
  }, [valueOne, getValueTwo]);

  // wrapping it as a ResponseType for convenience ;)
  return {
    data: valueTwo,
    error: errorOne || errorTwo,
    loading: loading || loadingOne || loadingTwo,
  };
};

This is incredibly ugly and I was hoping we could brainstorm a bit here to come up with a better alternative to my approach. My motivations to want to keep axios-hooks and not use axios only are:

Any ideas how can we make this better?

@PupoSDC
Copy link
Author

PupoSDC commented May 27, 2020

Apparentely you can await the get function returned by axios-hooks:

export default (valueZero: string): ResponseValues<string> => {
  const [{
    data: valueOne,
    error: errorOne,
    loading: loadingOne,
  }, getValueOne] = useAxios(`/api/${valueZero}`, {manual: true});

  const [{
    data: valueTwo = '',
    error: errorTwo,
    loading: loadingTwo,
  }, getValueTwo] = useAxios('', {manual: true});

  useEffect(() => {
    let isCancelled = false;  
    const getValueOneThanTwo = async () => {
        const response = await getValueOne(undefined, {useCache: true});
        const valueOne = response.data.valueOne;
        if (valueOne && !isCancelled) {
          getValueTwo(`api/${valueOne}`, {useCache: true});
        }
    }
   if (valueOne > 0) {
      getValueOneThanTwo()
    }
    return () => isCancelled = true;
  }, [valueZero, getValueOne, getValueTwo]);

  // wrapping it as a ResponseType for convenience ;)
  return {
    data: valueTwo,
    error: errorOne || errorTwo,
    loading: loadingOne || loadingTwo,
  };
};

it still looks a bit complicated. But it has improved the code. A perfect solution would be to somehow tell useAxios when to run the query in the qeury parameters. something like:

export default (valueZero: string): ResponseValues<string> => {
  const [{
    data: valueOne,
    error: errorOne,
    loading: loadingOne,
  }, getValueOne] = useAxios(`/api/${valueZero}`, {runWhen: () => valueZero.length});

  // this wont work, dont try to copy paste it
  const [{
    data: valueTwo = '',
    error: errorTwo,
    loading: loadingTwo,
  }, getValueTwo] = useAxios(`/api/${valueOne}`, {runWhen: () => valueOne.length});

  // wrapping it as a ResponseType for convenience ;)
  return {
    data: valueTwo,
    error: errorOne || errorTwo,
    loading: loadingOne || loadingTwo,
  };
};

But I'd need to dig into to the code to see if something like this is even possible, and know the creators opinion if it is something desirable at all

@simoneb
Copy link
Owner

simoneb commented May 27, 2020

This question was asked in this issue: #166. Unfortunately the link in my comment is gone so you can't see the code, but it's conceptually similar to what you did above.

Chaining multiple requests consists in awaiting the promise returned by the refetch function returned by useAxios.

I'll take note to:

  • document that the return value of that function is a promise
  • create an example for request chaining

As for the runWhen option, I'm thinking if that could somehow be achieved by extending the existing useCache option to accept a function instead.

@simoneb
Copy link
Owner

simoneb commented Jan 23, 2021

https://github.com/simoneb/axios-hooks/issues

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants