Tithos
Tithos

Reputation: 1417

How can I re-fetch an API using react hooks

devs,

I have decided to finally learn react hooks with what I thought would be a simple project. I can't quite figure out how I re-fetch an API using react hooks. Here is the code I have so far.

import React, { useState, useEffect } from "react"
import useFetch from "./utils/getKanya"

const kanye = "https://api.kanye.rest"

const Index = () => {
  let [kanyaQuote, setKanyeQuote] = useState(null)
  let data = useFetch(kanye)

  const getMore = () => {
    setKanyeQuote(useFetch(kanye))
  }

  return (
    <>
      <h1>Welcome to Next.js!</h1>
      <p>Here is a random Kanye West quote:</p>
      {!data ? <div>Loading...</div> : <p>{!kanyaQuote ? data : kanyaQuote}</p>}
      <button onClick={getMore}>Get new quote</button>
    </>
  )
}

export default Index
  1. I get the kanyeQuote state value to null
  2. I fetch the initial data
  3. I either show "Loading..." or the initial quote
  4. I am trying to set up a button to re-fetch the API and store the data in kanyeQuote via getKanyeQuote (setState)

This is the error I get Error: Invalid hook call...

I would greatly appreciate any guidance you can provide on this.

Upvotes: -1

Views: 553

Answers (1)

Linschlager
Linschlager

Reputation: 1639

The issue here is, that you can only use hooks directly inside the root of your component. It's the number 1 'rule of hooks'. You can read more about that here

  const getMore = () => {
    setKanyeQuote(useFetch(kanye) /* This cannot work! */)
  }

There are a few ways you could work around that. Without knowing the internal logic in your useFetch-hook I can only assume you are able to change it.

Change hook to handle its state internally

One way to work around that would be to change the logic of your custom useFetch hook to provide some form of function that fetches the data and updates the state internally. It could then look something like this:

const { data, doFetch } = useFetch(kanye);
useEffect(() => {
  doFetch(); // initialFetch
}, []);

const getMore = () => {
  doFetch();
};

// ...

You would then need to change the internal logic of your useFetch-hook to use useState internally and expose the getter of it. It would look something like this:

export const useFetch = (url) => {
  const [data, setData] = useState(null);

  const doFetch = () => {
    // Do your fetch-Logic
    setData(result);
  };

  return { data, doFetch };
};

Change hook not to handle any state at all.

If you only want to manage the state of the loaded data in the parent component, you could just provide the wrapped fetch function through the hook; Something like that:

const doFetch = useFetch(kanye);
const [data, setData] = useState(null);
useEffect(() => {
  setData(doFetch()); // initialFetch
}, []);

const getMore = () => {
  setData(doFetch())
};

// ...

You would then need to change the internal logic of your useFetch-hook to not have any internal state and just expose the wrapped fetch. It would look something like this:

export const useFetch = (url) => {  
  const doFetch = () => {
    // Do your fetch-Logic
    return result;
  };

  return doFetch;
};

Upvotes: 1

Related Questions