如何使用 React 功能组件通过循环回调获取 api
How to fetch api via looped callbacks with React functional components
所以我有一个 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" width="250px" 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 使用 [] 属性在控制台上显示数据,但不知道如何显示数据。任何帮助都会很棒。谢谢!
换一种方法可以吗?我会将显示图像放入其自己的组件中。
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" width="250px" src={`https://ipfs-asdf/${_tokenId}`} />
</div>
)
};
然后
return (
<div>
{array.map(index) => {
//Some Other Code//
<DisplayImage tokenId={tokenId} />
</div>
})
两件事对你的情况有用
在组件外声明的函数不会重新创建每个渲染
useState 和 useEffect 配对限制 调用 到 API 仅当 tokenId 更改时[=16] =]
// 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" width="250px" 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" width="250px" src={`https://ipfs-asdf/${tokenId}`} />
</div>
)
};
BTW 在 GetURI 这个
return (data.name || [])
看起来应该是
return data.name || ''
您可能应该缓存来自 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" width="250px" src={`https://ipfs-asdf/${tokenId}`} />
</div>
)
}
在大家的帮助下,我找到了解决问题的最佳方法,非常感谢!
我将 GetURI 函数放在显示图像组件中,并在每次有新令牌 ID 时调用 GetURI 的 useEffect 方法,然后我将状态变量设置为返回的任何值。
没有循环,没有错误
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" width="450px" 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>
})
所以我有一个 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" width="250px" 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 使用 [] 属性在控制台上显示数据,但不知道如何显示数据。任何帮助都会很棒。谢谢!
换一种方法可以吗?我会将显示图像放入其自己的组件中。
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" width="250px" src={`https://ipfs-asdf/${_tokenId}`} />
</div>
)
};
然后
return (
<div>
{array.map(index) => {
//Some Other Code//
<DisplayImage tokenId={tokenId} />
</div>
})
两件事对你的情况有用
在组件外声明的函数不会重新创建每个渲染
useState 和 useEffect 配对限制 调用 到 API 仅当 tokenId 更改时[=16] =]
// 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" width="250px" 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" width="250px" src={`https://ipfs-asdf/${tokenId}`} />
</div>
)
};
BTW 在 GetURI 这个
return (data.name || [])
看起来应该是
return data.name || ''
您可能应该缓存来自 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" width="250px" src={`https://ipfs-asdf/${tokenId}`} />
</div>
)
}
在大家的帮助下,我找到了解决问题的最佳方法,非常感谢!
我将 GetURI 函数放在显示图像组件中,并在每次有新令牌 ID 时调用 GetURI 的 useEffect 方法,然后我将状态变量设置为返回的任何值。
没有循环,没有错误
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" width="450px" 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>
})