如何使用 React 功能组件通过循环回调获取 api

Posted

技术标签:

【中文标题】如何使用 React 功能组件通过循环回调获取 api【英文标题】:How to fetch api via looped callbacks with React functional components 【发布时间】:2021-10-14 09:49:22 【问题描述】:

所以我有一个 40+ 循环调用另一个组件来显示图像。每个图像都有一个 ID,使用该 ID,我可以通过另一个 API 调用获取有关图像的更多信息,例如名称和描述。

DisplayImage 被调用时,我希望它调用另一个回调函数,该函数将为该图像的元数据发送 API 调用,将其存储在变量中并将其显示为 H1 标记。

return (
 <div>

  array.map(index) => 

   // Some Other Code That return a TokenID //

   <>
    displayImage(tokenId)
   </>
 
 </div>
)
 const displayImage = (tokenId) => 

  const imageName = GetURI(tokenId)

  return (
      <div className="token-container">
        <h1>imageName</h1>
        <img className="artwork"  src=`https://ipfs-asdf/$tokenId` />
      </div>
  )

 

const GetURI = async (tokenId) => 

    const res = await fetch("https://api"+tokenId , 
        headers: 
          'Content-Type': 'application/json',
          'Accept': 'application/json'
        ,
    ).then(data => 
      console.log(data)
      return data.json();
    )
    .then(data => 
      return (data.name || [])
    )
    .catch(err => 
      console.log(err);
    );

数据正在控制台上显示,但现在我遇到了一个无限循环问题,我知道 UseEffect 可以解决,但我无法弄清楚。我设法使用 [] 属性通过 UseEffect 在控制台上显示数据,但不知道如何显示数据。任何帮助都会很棒。谢谢!

【问题讨论】:

尝试用useCallback包装你的GetURI函数 您可以为您的用例找到这个article interesting。它解释了如何将反应式编程与 React 功能组件一起使用。 @Picci 我没有发现这有帮助。他似乎在努力磨练而不是出去。我也有这个无限循环问题。我的前端看起来不错,但很快我什至无法检查页面。有人可以公然解决这种严重的循环吗? 【参考方案1】:

在大家的帮助下,我找到了最好的方法,所以谢谢!

我将 GetURI 函数放在显示图像组件中,并且每次有新的令牌 ID 时都会调用一个 useEffect 方法调用 GetURI,然后我将状态变量设置为返回的任何内容。

没有循环,没有错误?



const DisplayImage = (data) => 

  const [nftMetadata, setNftMetadata] = useState();

  const GetURI = async (data) => 
     const nftURI = await data.drizzle.contracts.Contract.methods.tokenURI(data.tokenId).call()

      await fetch(nftURI , 
        headers: 
          'Content-Type': 'application/json',
          'Accept': 'application/json',
          "Access-Control-Allow-Origin": "*"
        ,
        )
        .then(data => 
          return data.json();
        )
        .then(data => 
          return setNftMetadata(data || []);
        )
        .catch(err => 
          return console.log(err);
      );
  );

  useEffect(() => 
    GetURI(data);
  , [data.tokenId])   
  
  return (
    <div className="token-container">

     <h2>nftMetadata.name</h2>
     <img className="artwork"  src=`https://ipfs:/whatever/$nftMetadata.image` />

    </div>
  );

;

return (
 <div>

  array.map(index) => 

   // Some Other Code That returns a TokenID //

   <>
    <DisplayImage address=drizzle.contractList[0].address tokenId=tokenId drizzle=drizzle drizzleState=drizzleState/>
   </>
 
 </div>
)

【讨论】:

【参考方案2】:

您可能应该缓存来自 GetURI(tokenId) 的响应。使用相同的 tokenId 时无需两次询问相同的 URI。 一个简单的方法是使用react-query:

在 App.js 中设置:

 // App.js
 import  QueryClient, QueryClientProvider  from 'react-query'

 const queryClient = new QueryClient()

 export default function App() 
   return (
     <QueryClientProvider client=queryClient>
       <Example />
     </QueryClientProvider>
   )
 

然后在 DisplayImage 组件中使用(而不是内联函数):

// DisplayImage.js
import  useQuery  from 'react-query'

export function DisplayImage(tokenId) 
  const  isLoading, error, data: imageName  = useQuery(['images', tokenId], GetURI(tokenId))
  return (
    <div className="token-container">
      <h1>isLoading ? 'loading...' : imageName</h1>
      <img className="artwork"  src=`https://ipfs-asdf/$tokenId` />
    </div>
  )

【讨论】:

【参考方案3】:

对你的情况有用的两件事

在组件外部声明的函数不会重新创建每次渲染

useState 和 useEffect 配对将调用限制为仅在 tokenId 更改时才调用 API

// Put this function outside the component
// so it does not need a useCallback
// i.e not reconstructed each render of DisplayImage

const GetURI = async (tokenId) => 
  ...
);

const DisplayImage = (tokenId) => 

  const [imageName, setImageName] = useState()

  // limit calls to API to when tokenId changes
  // and if eslint complains add GetURI to dependency list
  // - but GetURI never changes, so no un-needed calls from it
  useEffect(() => 
    setImageName(GetURI(tokenId))
  , [tokenId, GetURI])   
  
  return (
    <div className="token-container">
      <h2>imageName</h2>
      <img className="artwork"  src=`https://ipfs-asdf/$tokenId` />
    </div>
  )
;

你也可以抽象成自定义钩子useImageName()

const GetURI = async (tokenId) => 
  ...
);

const useImageName = (tokenId) => 

  const [imageName, setImageName] = useState()

  useEffect(() => 
    setImageName(GetURI(tokenId))
  , [tokenId, GetURI])   

  return imageName
)
const DisplayImage = (tokenId) => 

  const imageName = useImageName(tokenId)

  return (
    <div className="token-container">
      <h2>imageName</h2>
      <img className="artwork"  src=`https://ipfs-asdf/$tokenId` />
    </div>
  )
;

顺便说一句,在 GetURI 中

return (data.name || [])

看起来应该是

return data.name || ''

【讨论】:

【参考方案4】:

不同的方法可以吗?我会将显示图像放入它自己的组件中。

const DisplayImage = (tokenId: _tokenId) => 

  const imageName = GetURI(_tokenId)
  
  const GetURI = useCallback(async () => 
      await fetch("https://api"+tokenId , 
            headers: 
              'Content-Type': 'application/json',
              'Accept': 'application/json'
            ,
        ).then(data => 
          console.log(data)
          return data.json();
        )
        .then(data => 
          return (data.name || [])
        )
        .catch(err => 
          console.log(err);
        );
      )
    );

    useEffect(() => 
      if (_tokenId) GetURI();
    , [GetURI]);

  return (
      <div className="token-container">
        <h2>imageName</h2>
        <img className="artwork"  src=`https://ipfs-asdf/$_tokenId` />
      </div>
  )
;

然后

return (
 <div>

  array.map(index) => 

   //Some Other Code//

   <DisplayImage tokenId=tokenId />
 
 </div>
)

【讨论】:

不会 useEffect 只在挂载时调用该函数吗?我打算多次调用这个函数。我有 75 个不同的令牌 ID 将调用此 API。谢谢。 我用不同的方法编辑了答案。希望有帮助 不幸的是没有工作。我收到一个错误 - 在初始化之前无法访问“Ge​​tURI” - 是否允许这样使用挂钩?

以上是关于如何使用 React 功能组件通过循环回调获取 api的主要内容,如果未能解决你的问题,请参考以下文章

React:如何使用功能性 usestate/useEffect 复制特定的组件类 setstate 回调示例?

如何在功能性 React JS 组件中获取数据?

在hook里面使用回调refs

[React] 子组件向父组件通信:回调函数

useReducer 和 useState

React组件间通信