Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

278
Views
How to fetch data from a custom React hook (API) with onClick and display it in a Div using TypeScript?

I'm new to TypeScript and even React Hooks. I'm trying to fetch data continuously from a custom hook called useFetch(), which takes in a string (the URL) parameter:

import { useEffect, useState } from "react";

export interface Payload {
  data: null | string,
  loading: boolean
}

const useFetch = (url: string) => {
  const [state, setState] = useState<Payload>({data: null, loading: true})

  useEffect(() => {
    setState(state => ({ data: state.data, loading: true }));
    fetch(url)
      .then(x => x.text())
      .then(y => {
        setState({ data: y, loading: false });
      });
  }, [url, setState])

  return state;
  }

export default useFetch;

I import that hook into App():

import React, { useState, useEffect } from "react";
import useFetch from './utils/useFetch'
import {Payload} from './utils/useFetch';

const App = () => {
  const [quote, setquote] = useState<Payload>({data: null, loading: true})
  const handleClick = () => setquote(data)

  const data = useFetch(
    "/api/rand"
  );

  return (
    <div>
      <h1>Quotes</h1>
      <button onClick={handleClick}>Get Quote</button>
      <div>{quote.data}</div>
    </div>
  )
}

export default App;

On the first time the app loads, it works. I get data (a quote) when I click the button.

Preview

However, when I click it multiple times, the API isn't called again, and new data doesn't come through. I believe I'm supposed to use the useEffect() react hook and maintain the state (maybe I'm mistaken) but all my attempts so far have been to no avail. Any help would be much appreciated. If I figure it out, I'll definitely answer this question. Thanks!

about 4 years ago · Juan Pablo Isaza
3 answers
Answer question

0

It seems like you are mixing two ideas a little bit here.

Do you want to fetch data continuously? (Continuously meaning all the time, as in, periodically, at an interval)

Or do you want to fetch data upon mount of the component (currently happens) AND when the user taps the "Get Quote" button (not working currently)?

I will try to help you with both.

Continuously / periodically

Try changing the setup a bit. Inside the useFetch do something like:

import { useEffect, useState } from "react";

export interface Payload {
  data: null | string,
  loading: boolean
}

const useFetch = (url: string) => {
  const [state, setState] = useState<Payload>({data: null, loading: true})

  useEffect(() => {
    const interval = setInterval(() => {
        setState(state => ({ data: state.data, loading: true }));
        fetch(url)
          .then(x => x.text())
          .then(y => {
            setState({ data: y, loading: false });
          });
     }, 2000); // Something like 2s

     return () => {
        clearInterval(interval); // clear the interval when component unmounts
     }
  }, [url])

  return state;
  }

export default useFetch;

This will fetch the endpoint every 2s and update the state variable, this will then be reflected in the App (or any place that uses the hook for that matter).

Therefore you do not need the quote state anymore in the App. Also, you don't need a button in the UI anymore.

App will looks something like:

import React, { useState, useEffect } from "react";
import useFetch from './utils/useFetch'
import {Payload} from './utils/useFetch';

const App = () => {
  const quote = useFetch(
    "/api/rand"
  );

  return (
    <div>
      <h1>Quotes</h1>
      <div>{quote.data}</div>
    </div>
  )
}

export default App;

Fetch data upon mount of the component (currently happens) AND when the user taps the "Get Quote" button

Then the useFetch hook isn't really needed/suited in my opinion. A hook can be used for this application, but transforming it into a more simple function would make more sense to me. I suggest omitting the useFetch hook. The App component would look something like:

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

const App = () => {
  const [quote, setQuote] = useState<Payload>({data: null, loading: true})

  const handleGetQuote = useCallback(() => {
    setQuote(state => ({ data: state.data, loading: true }));
    fetch("/api/rand")
      .then(x => x.text())
      .then(y => {
        setQuote({ data: y, loading: false });
      });
  }, []);

  useEffect(() => {
    handleGetQuote();
  }, [handleGetQuote]);

  return (
    <div>
      <h1>Quotes</h1>
      <button onClick={handleGetQuote}>Get Quote</button>
      <div>{quote.data}</div>
    </div>
  )
}

export default App;
about 4 years ago · Juan Pablo Isaza Report

0

Here's another possibility: I added a reload function to the return values of useFetch. Also, since the useFetch hook already contains its own state, I removed the useState from App

const useFetch = (url: string) => {
  const [state, setState] = useState<Payload>({data: null, loading: true})
  const [reloadFlag, setReloadFlag] = useState(0)

  useEffect(() => {
    if(reloadFlag !== 0){ // remove this if you want the hook to fetch data initially, not just after reload has been clicked
      setState(state => ({ data: state.data, loading: true }));
      fetch(url)
        .then(x => x.text())
        .then(y => {
          setState({ data: y, loading: false });
        });
    }
  }, [url, setState, reloadFlag])

  return [state, ()=>setReloadFlag((curFlag)=>curFlag + 1)];
  }

const App = () => {

  const [data, reload] = useFetch(
    "/api/rand"
  );

  return (
    <div>
      <h1>Quotes</h1>
      <button onClick={reload}>Get Quote</button>
      <div>{quote.data}</div>
    </div>
  )
}
about 4 years ago · Juan Pablo Isaza Report

0

Just change the code as below:

const useFetch = (url: string) => {
  const [state, setState] = useState<Payload>({data: null, loading: true})

  setState(state => ({ data: state.data, loading: true }));
    fetch(url)
      .then(x => x.text())
      .then(y => {
        setState({ data: y, loading: false });
      });

  return state;
  }

export default useFetch;
about 4 years ago · Juan Pablo Isaza Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!