【问题标题】:How to fetch data from a custom React hook (API) with onClick and display it in a Div using TypeScript?如何使用 onClick 从自定义 React 钩子 (API) 中获取数据并使用 TypeScript 将其显示在 Div 中?
【发布时间】:2021-10-04 09:48:08
【问题描述】:

我是 TypeScript 甚至 React Hooks 的新手。我正在尝试从一个名为useFetch() 的自定义钩子中连续获取数据,该钩子接受一个字符串(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 钩子并保持状态(也许我错了),但到目前为止我所有的尝试都无济于事。任何帮助将非常感激。如果我弄清楚了,我肯定会回答这个问题。谢谢!

【问题讨论】:

  • 是应该从同一个 url 捕获一个新的引用,还是在每次点击时调用一个不同的 url?
  • @henk 是同一个网址。

标签: javascript reactjs typescript react-hooks


【解决方案1】:

您似乎在这里混合了两个想法。

您想连续获取数据吗? (连续的意思是一直,如in,周期性,间隔)

或者您想在组件挂载(当前发生)并且当用户点击“获取报价”按钮(当前不工作)时获取数据?

我会尽力帮助你。

连续/定期

尝试稍微改变一下设置。在 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 秒获取一次端点并更新状态变量,然后这将反映在应用程序中(或任何使用该钩子的地方)。

因此,您不再需要应用程序中的报价状态。此外,您不再需要 UI 中的按钮。

应用看起来像:

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 钩子。 App 组件看起来像:

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 钩子已经包含它自己的状态,我从App 中删除了useState

      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;
        

        【讨论】:

          猜你喜欢
          • 2020-11-07
          • 2022-01-07
          • 2020-06-20
          • 2020-06-03
          • 2021-07-03
          • 2021-07-07
          • 1970-01-01
          • 2020-08-27
          • 2021-09-24
          相关资源
          最近更新 更多