Как извлечь данные из пользовательского интерфейса React (API) с помощью onClick и отобразить их в Div с помощью машинописного текста?

#javascript #reactjs #typescript #react-hooks

Вопрос:

Я новичок в машинописи и даже реагирую на крючки. Я пытаюсь непрерывно извлекать данные из пользовательского вызываемого крючка useFetch() , который принимает параметр string (URL) :

 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;
 

Я импортирую этот крючок в 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;
 

При первой загрузке приложения оно работает. Я получаю данные (цитату), когда нажимаю на кнопку.

Предварительный просмотр

Однако, когда я нажимаю на него несколько раз, API больше не вызывается, и новые данные не поступают. Я считаю, что я должен использовать useEffect() крюк react и поддерживать состояние (возможно, я ошибаюсь), но все мои попытки до сих пор были безрезультатны. Любая помощь была бы очень признательна. Если я это выясню, я обязательно отвечу на этот вопрос. Спасибо!

Комментарии:

1. должен ли он перехватывать новую цитату с того же URL-адреса или звонить по другому URL-адресу при каждом щелчке?

2. @henk Это тот же URL-адрес.

Ответ №1:

Похоже, вы немного смешиваете здесь две идеи.

Вы хотите получать данные непрерывно? (Непрерывно, то есть все время, как в, периодически, с интервалом)

Или вы хотите получить данные при монтировании компонента (в настоящее время происходит) И когда пользователь нажимает кнопку «Получить котировку» (в настоящее время не работает)?

Я постараюсь помочь вам и с тем, и с другим.

Непрерывно / периодически

Попробуйте немного изменить настройку. Внутри useFetch сделайте что-нибудь вроде:

 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;
 

Это будет извлекать конечную точку каждые 2 секунды и обновлять переменную состояния, затем это будет отражено в приложении (или в любом месте, где используется крючок, если на то пошло).

Поэтому вам больше не нужно состояние котировки в приложении. Кроме того, вам больше не нужна кнопка в пользовательском интерфейсе.

Приложение будет выглядеть примерно так:

 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;
 

Получение данных при монтировании компонента (в настоящее время происходит) И когда пользователь нажимает кнопку «Получить цитату»

Тогда, на мой взгляд, крючок useFetch на самом деле не нужен/не подходит. Для этого приложения можно использовать крюк, но преобразование его в более простую функцию имело бы для меня больше смысла. Я предлагаю опустить крючок useFetch. Компонент приложения будет выглядеть примерно так:

 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;
 

Ответ №2:

Проблема здесь в том, что вы не переставляете кавычки.

 const useFetch = (url:string) => {
  const [loading, setLoading] = useState(false);
  const [quote, setQuote] = useState('');

  const refetch = useCallback(() => {
    setLoading(true);
    fetch(url)
      .then((x) => x.text())
      .then((y) => {
        //setQuote(y);
        setQuote(String(Math.random()));
        setLoading(false);
      });
  }, [url]);

  return { quote, refetch, loading };
};

const App = () => {
  const { quote, refetch, loading } = useFetch('/api/rand');

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

  return (
    <div>
      <h1>Quotes</h1>
      <button onClick={refetch}>Get Quote</button>
      {loading ? ' loading...' : ''}
      <div>{quote}</div>
    </div>
  );
};
 

Смотрите этот пример
https://stackblitz.com/edit/react-ts-kdyptk?file=index.tsx

Ответ №3:

Вот еще одна возможность: я добавил функцию перезагрузки к возвращаемым значениям useFetch . Кроме того, поскольку useFetch крючок уже содержит свое собственное состояние, я удалил useState из 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>
  )
}
 

Ответ №4:

Просто измените код, как показано ниже:

 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;